Class: RatatuiRuby::Event::Key
- Inherits:
-
RatatuiRuby::Event
- Object
- RatatuiRuby::Event
- RatatuiRuby::Event::Key
- Defined in:
- lib/ratatui_ruby/event/key.rb,
lib/ratatui_ruby/event/key/dwim.rb,
lib/ratatui_ruby/event/key/media.rb,
lib/ratatui_ruby/event/key/system.rb,
lib/ratatui_ruby/event/key/modifier.rb,
lib/ratatui_ruby/event/key/character.rb,
lib/ratatui_ruby/event/key/navigation.rb
Overview
Captures a keyboard interaction.
The keyboard is the primary interface for your terminal application. Raw key codes are often cryptic, and handling modifiers manually is error-prone.
This event creates clarity. It encapsulates the interaction, providing a normalized code and a list of active modifiers.
Compare it directly to strings or symbols for rapid development, or use pattern matching for complex control schemes.
Examples
Using predicates: – SPDX-SnippetBegin SPDX-FileCopyrightText: 2025 Kerrick Long SPDX-License-Identifier: MIT-0 ++
if event.key? && event.ctrl? && event.code == "c"
exit
end
– SPDX-SnippetEnd ++ Using symbol comparison: – SPDX-SnippetBegin SPDX-FileCopyrightText: 2025 Kerrick Long SPDX-License-Identifier: MIT-0 ++
if event == :ctrl_c
exit
end
– SPDX-SnippetEnd ++ Using pattern matching: – SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
case event
in type: :key, code: "c", modifiers: ["ctrl"]
exit
end
– SPDX-SnippetEnd ++
Terminal Compatibility
Some key combinations never reach your application. Terminal emulators intercept them for built-in features like tab switching. Common culprits:
-
Ctrl+PageUp/PageDown (tab switching in Terminal.app, iTerm2)
-
Ctrl+Tab (tab switching)
-
Cmd+key combinations (macOS system shortcuts)
If modifiers appear missing, test with a different terminal. Kitty, WezTerm, and Alacritty pass more keys through. See doc/terminal_limitations.md for details.
Enhanced Keys (Kitty Protocol)
Terminals supporting the Kitty keyboard protocol report additional keys:
-
Media keys:
:play,:play_pause,:track_next,:mute_volume -
Individual modifiers:
:left_shift,:right_control,:left_super
These keys will not work in Terminal.app, iTerm2, or GNOME Terminal.
Defined Under Namespace
Modules: Character, Dwim, Media, Modifier, Navigation, System
Constant Summary
Constants included from Dwim
Dwim::ARROW_KEYS, Dwim::NAVIGATION_KEYS, Dwim::PUNCTUATION_NAMES, Dwim::VIM_MOVEMENT_KEYS
Instance Attribute Summary collapse
-
#code ⇒ Object
readonly
The key code (e.g.,
"a","enter","up"). -
#kind ⇒ Object
readonly
The category of the key.
-
#modifiers ⇒ Object
readonly
List of active modifiers (
"ctrl","alt","shift").
Instance Method Summary collapse
-
#==(other) ⇒ Object
Compares the event with another object.
-
#deconstruct_keys(keys) ⇒ Object
Deconstructs the event for pattern matching.
-
#initialize(code:, modifiers: [], kind: :standard) ⇒ Key
constructor
Creates a new Key event.
-
#inspect ⇒ Object
Returns inspection string.
-
#key? ⇒ Boolean
Returns true for Key events.
-
#method_missing(name, *args, **kwargs, &block) ⇒ Object
Supports dynamic key predicate methods via method_missing.
-
#respond_to_missing?(name, *args) ⇒ Boolean
Declares that this class responds to dynamic predicate methods.
-
#to_s ⇒ Object
Converts the event to its String representation.
-
#to_sym ⇒ Object
Converts the event to a Symbol representation.
Methods included from System
Methods included from Navigation
Methods included from Modifier
#alt?, #ctrl?, #hyper?, #meta?, #modifier?, #shift?, #super?
Methods included from Media
Methods included from Dwim
#alphanumeric?, #arrow?, #cancel?, #cr?, #digit?, #eof?, #interrupt?, #letter?, #navigation?, #punctuation?, #quit?, #quote?, #sigint?, #space?, #suspend?, #vim?, #vim_bottom?, #vim_down?, #vim_left?, #vim_right?, #vim_top?, #vim_up?, #vim_word_backward?, #vim_word_forward?, #whitespace?
Methods included from Character
Methods inherited from RatatuiRuby::Event
#focus_gained?, #focus_lost?, #mouse?, #none?, #paste?, #resize?, #sync?
Constructor Details
#initialize(code:, modifiers: [], kind: :standard) ⇒ Key
Creates a new Key event.
- code
-
The key code (String).
- modifiers
-
List of modifiers (Array<String>).
- kind
-
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
The key category (Symbol). One of: <tt>:standard</tt>, <tt>:function</tt>, <tt>:media</tt>, <tt>:modifier</tt>, <tt>:system</tt>. Defaults to <tt>:standard</tt>.– SPDX-SnippetEnd ++
151 152 153 154 155 |
# File 'lib/ratatui_ruby/event/key.rb', line 151 def initialize(code:, modifiers: [], kind: :standard) @code = code.freeze @modifiers = modifiers.map(&:freeze).sort.freeze @kind = kind end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, **kwargs, &block) ⇒ Object
Supports dynamic key predicate methods via method_missing.
Allows convenient checking for specific keys or key combinations:
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2025 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.ctrl_c? # => true if Ctrl+C
event.enter? # => true if Enter
event.shift_up? # => true if Shift+Up
event.q? # => true if "q"
– SPDX-SnippetEnd ++ The method name is converted to a symbol and compared against the event. This works for any key code or modifier+key combination.
Smart Predicates (DWIM)
For convenience, generic predicates match both system and media variants:
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.pause? # => true for BOTH system "pause" AND "media_pause"
event.play? # => true for "media_play"
event.stop? # => true for "media_stop"
– SPDX-SnippetEnd ++ This “Do What I Mean” behavior reduces boilerplate when you just want to respond to a conceptual action (e.g., “pause the playback”) regardless of whether the user pressed a keyboard key or a media button.
For strict matching, use the full predicate or compare the code directly:
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.media_pause? # => true ONLY for media pause
event.code == "pause" # => true ONLY for system pause
– SPDX-SnippetEnd ++
Arrow Key Aliases
Arrow keys respond to arrow_up? and up_arrow? variants. This disambiguates from Mouse events, which also respond to up? and down?:
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.arrow_up? # => true for up arrow key
event.up_arrow? # => true for up arrow key
event.arrow_down? # => true for down arrow key
– SPDX-SnippetEnd ++
Key Prefix and Suffix
Predicates accept key_ prefix or _key suffix for explicit key event matching in mixed event contexts:
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.key_up? # => true for up arrow key
event.key_q? # => true for "q" key
event.q_key? # => true for "q" key
event.enter_key? # => true for enter key
– SPDX-SnippetEnd ++
Capital Letters and Shift
Capital letter predicates match shifted keys naturally. The terminal reports the produced character with shift in the modifiers:
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.G? # => true for code="G" modifiers=["shift"]
event.shift_g? # => true for code="G" modifiers=["shift"]
event.alt_B? # => true for code="B" modifiers=["alt", "shift"]
– SPDX-SnippetEnd ++
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/ratatui_ruby/event/key.rb', line 389 def method_missing(name, *args, **kwargs, &block) if name.to_s.end_with?("?") name_str = name.to_s key_name = name_str.chop # Returns String, never nil for non-empty string key_sym = key_name.to_sym # Fast path: Exact match (e.g., media_pause? for media_pause) return true if self == key_sym # Delegate category-specific DWIM logic to mixins return true if match_media_dwim?(key_name) return true if match_modifier_dwim?(key_name, key_sym) return true if (key_name, key_sym) return true if match_system_dwim?(key_name, key_sym) # DWIM: key_ prefix and _key suffix (disambiguate from mouse events) # key_up? → up?, q_key? → q?, etc. key_name = key_name.delete_prefix("key_").delete_suffix("_key") # Fast path after prefix/suffix stripping return true if self == key_name.to_sym # DWIM: Single character codes match even with shift modifier present # G? matches code="G" modifiers=["shift"], B? matches code="B" modifiers=["alt","shift"] # @? matches code="@" modifiers=["shift"] # The terminal reports the produced character, shift is implicit for these if key_name.length == 1 && @code == key_name && @modifiers.include?("shift") return true end # DWIM: Uppercase in predicate implies shift, so alt_B? matches alt_shift_B # Parse predicate to extract modifiers and final letter if key_name.match?(/\A([a-z_]+_)?([A-Z])\z/) pred_letter = key_name[-1] pred_mods = key_name.chop.delete_suffix("_").split("_").reject(&:empty?) expected_mods = (pred_mods + ["shift"]).sort return true if @code == pred_letter && @modifiers == expected_mods end # DWIM: Case-insensitive letter matching with modifiers # shift_g? matches code="G" modifiers=["shift"] if @code.length == 1 && @code.match?(/[A-Za-z]/) && (to_sym.to_s.downcase == key_name.downcase) return true end # DWIM: Universal underscore-insensitivity # Normalize both predicate and code by stripping underscores normalized_predicate = key_name.delete("_") normalized_code = @code.delete("_") return true if normalized_predicate == normalized_code && @modifiers.empty? # DWIM: Underscore variants delegate to existing methods # space_bar? → spacebar? → space?, sig_int? → sigint? normalized_method = :"#{normalized_predicate}?" if normalized_method != name && respond_to?(normalized_method) return public_send(normalized_method) end false else super end end |
Instance Attribute Details
#code ⇒ Object (readonly)
The key code (e.g., "a", "enter", "up").
puts event.code # => "enter"
101 102 103 |
# File 'lib/ratatui_ruby/event/key.rb', line 101 def code @code end |
#kind ⇒ Object (readonly)
The category of the key.
One of: :standard, :function, :media, :modifier, :system.
This allows grouping keys by their logical type without parsing the code string.
event.kind # => :media
115 116 117 |
# File 'lib/ratatui_ruby/event/key.rb', line 115 def kind @kind end |
#modifiers ⇒ Object (readonly)
List of active modifiers ("ctrl", "alt", "shift").
puts event.modifiers # => ["ctrl", "shift"]
106 107 108 |
# File 'lib/ratatui_ruby/event/key.rb', line 106 def modifiers @modifiers end |
Instance Method Details
#==(other) ⇒ Object
Compares the event with another object.
-
If
otheris aSymbol, compares against #to_sym. -
If
otheris aString, compares against #to_s. -
If
otheris aKey, compares as a value object. -
Otherwise, compares using standard equality.
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/ratatui_ruby/event/key.rb', line 163 def ==(other) case other when Symbol to_sym == other when String to_s == other when Key code == other.code && modifiers == other.modifiers else super end end |
#deconstruct_keys(keys) ⇒ Object
Deconstructs the event for pattern matching.
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
case event
in type: :key, code: "c", modifiers: ["ctrl"]
puts "Ctrl+C pressed"
in type: :key, kind: :media
puts "Media key pressed"
end
– SPDX-SnippetEnd ++
474 475 476 |
# File 'lib/ratatui_ruby/event/key.rb', line 474 def deconstruct_keys(keys) { type: :key, code: @code, modifiers: @modifiers, kind: @kind } end |
#inspect ⇒ Object
Returns inspection string.
279 280 281 |
# File 'lib/ratatui_ruby/event/key.rb', line 279 def inspect "#<#{self.class} code=#{@code.inspect} modifiers=#{@modifiers.inspect} kind=#{@kind.inspect}>" end |
#key? ⇒ Boolean
Returns true for Key events.
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2025 Kerrick Long SPDX-License-Identifier: MIT-0 ++
event.key? # => true
event.mouse? # => false
event.resize? # => false
– SPDX-SnippetEnd ++
130 131 132 |
# File 'lib/ratatui_ruby/event/key.rb', line 130 def key? true end |
#respond_to_missing?(name, *args) ⇒ Boolean
Declares that this class responds to dynamic predicate methods.
454 455 456 |
# File 'lib/ratatui_ruby/event/key.rb', line 454 def respond_to_missing?(name, *args) name.to_s.end_with?("?") || super end |
#to_s ⇒ Object
Converts the event to its String representation.
- Printable Characters
-
Returns the character itself (e.g.,
"a","1"," "). - Special Keys
-
Returns an empty string (e.g.,
"enter","up","f1"all return""). - Modifiers
-
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2025 Kerrick Long SPDX-License-Identifier: MIT-0 ++
Returns the character if printable, ignoring modifiers unless they alter the character code itself. Note that <tt>ctrl+c</tt> typically returns <tt>"c"</tt> as the code, so +to_s+ will return <tt>"c"</tt>.– SPDX-SnippetEnd ++
270 271 272 273 274 275 276 |
# File 'lib/ratatui_ruby/event/key.rb', line 270 def to_s if text? @code else "" end end |
#to_sym ⇒ Object
Converts the event to a Symbol representation.
The format is [modifiers_]code. Modifiers are sorted alphabetically (alt, ctrl, shift) and joined by underscores.
Supported Keys
- Standard
-
:enter,:backspace,:tab,:back_tab,:esc,:null - Navigation
-
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
<tt>:up</tt>, <tt>:down</tt>, <tt>:left</tt>, <tt>:right</tt>, <tt>:home</tt>, <tt>:end</tt>, <tt>:page_up</tt>, <tt>:page_down</tt>, <tt>:insert</tt>, <tt>:delete</tt>– SPDX-SnippetEnd ++
- Function Keys
-
:f1through:f12(and beyond, e.g.:f24) - Lock Keys
-
:caps_lock,:scroll_lock,:num_lock - System Keys
-
:print_screen,:pause,:menu,:keypad_begin - Media Keys
-
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
<tt>:play</tt>, <tt>:media_pause</tt>, <tt>:play_pause</tt>, <tt>:reverse</tt>, <tt>:stop</tt>, <tt>:fast_forward</tt>, <tt>:rewind</tt>, <tt>:track_next</tt>, <tt>:track_previous</tt>, <tt>:record</tt>, <tt>:lower_volume</tt>, <tt>:raise_volume</tt>, <tt>:mute_volume</tt>– SPDX-SnippetEnd ++
- Modifier Keys
-
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
<tt>:left_shift</tt>, <tt>:left_control</tt>, <tt>:left_alt</tt>, <tt>:left_super</tt>, <tt>:left_hyper</tt>, <tt>:left_meta</tt>, <tt>:right_shift</tt>, <tt>:right_control</tt>, <tt>:right_alt</tt>, <tt>:right_super</tt>, <tt>:right_hyper</tt>, <tt>:right_meta</tt>, <tt>:iso_level3_shift</tt>, <tt>:iso_level5_shift</tt>– SPDX-SnippetEnd ++
- Characters
-
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2025 Kerrick Long SPDX-License-Identifier: MIT-0 ++
<tt>:a</tt>, <tt>:b</tt>, <tt>:1</tt>, <tt>:space</tt>, etc.– SPDX-SnippetEnd ++
Modifier Examples
-
:ctrl_c -
:alt_enter -
:shift_left -
:ctrl_alt_delete
-
244 245 246 247 248 249 250 251 |
# File 'lib/ratatui_ruby/event/key.rb', line 244 def to_sym mods = @modifiers.join("_") if mods.empty? @code.to_sym else :"#{mods}_#{@code}" end end |