class RatatuiRuby::Event::Key
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:
if event.key? && event.ctrl? && event.code == "c" exit end
Using symbol comparison:
if event == :ctrl_c exit end
Using pattern matching:
case event in type: :key, code: "c", modifiers: ["ctrl"] exit end
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:
-
Mediakeys::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.
Attributes
The key code (e.g., "a", "enter", "up").
puts event.code # => "enter"
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
List of active modifiers ("ctrl", "alt", "shift").
puts event.modifiers # => ["ctrl", "shift"]
Public Class Methods
Source
# 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
Creates a new Key event.
- code
-
The key code (String).
- modifiers
-
List of modifiers (Array<String>).
- kind
-
The key category (Symbol). One of:
:standard,:function,:media,:modifier,:system. Defaults to:standard.
Public Instance Methods
Source
# 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
Source
# File lib/ratatui_ruby/event/key.rb, line 474 def deconstruct_keys(keys) { type: :key, code: @code, modifiers: @modifiers, kind: @kind } end
Deconstructs the event for pattern matching.
case event in type: :key, code: "c", modifiers: ["ctrl"] puts "Ctrl+C pressed" in type: :key, kind: :media puts "Media key pressed" end
Source
# File lib/ratatui_ruby/event/key.rb, line 279 def inspect "#<#{self.class} code=#{@code.inspect} modifiers=#{@modifiers.inspect} kind=#{@kind.inspect}>" end
Returns inspection string.
Source
# File lib/ratatui_ruby/event/key.rb, line 130 def key? true end
Returns true for Key events.
event.key? # => true event.mouse? # => false event.resize? # => false
Source
# 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 match_navigation_dwim?(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
Supports dynamic key predicate methods via method_missing.
Allows convenient checking for specific keys or key combinations:
event.ctrl_c? # => true if Ctrl+C event.enter? # => true if Enter event.shift_up? # => true if Shift+Up event.q? # => true if "q"
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:
event.pause? # => true for BOTH system "pause" AND "media_pause" event.play? # => true for "media_play" event.stop? # => true for "media_stop"
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:
event.media_pause? # => true ONLY for media pause event.code == "pause" # => true ONLY for system pause
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?:
event.arrow_up? # => true for up arrow key event.up_arrow? # => true for up arrow key event.arrow_down? # => true for down arrow key
Key Prefix and Suffix
Predicates accept key_ prefix or _key suffix for explicit key event matching in mixed event contexts:
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
Capital Letters and Shift
Capital letter predicates match shifted keys naturally. The terminal reports the produced character with shift in the modifiers:
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"]
RatatuiRuby::Event#method_missing
Source
# File lib/ratatui_ruby/event/key.rb, line 454 def respond_to_missing?(name, *args) name.to_s.end_with?("?") || super end
Declares that this class responds to dynamic predicate methods.
RatatuiRuby::Event#respond_to_missing?
Source
# File lib/ratatui_ruby/event/key.rb, line 270 def to_s if text? @code else "" end end
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
-
Returns the character if printable, ignoring modifiers unless they alter the character code itself. Note that
ctrl+ctypically returns"c"as the code, soto_swill return"c".
Source
# 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
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-
:up,:down,:left,:right,:home,:end,:page_up,:page_down,:insert,:delete - Function Keys
-
:f1through:f12(and beyond, e.g.:f24) - Lock Keys
-
:caps_lock,:scroll_lock,:num_lock -
SystemKeys -
:print_screen,:pause,:menu,:keypad_begin -
MediaKeys -
:play,:media_pause,:play_pause,:reverse,:stop,:fast_forward,:rewind,:track_next,:track_previous,:record,:lower_volume,:raise_volume,:mute_volume -
ModifierKeys -
:left_shift,:left_control,:left_alt,:left_super,:left_hyper,:left_meta,:right_shift,:right_control,:right_alt,:right_super,:right_hyper,:right_meta,:iso_level3_shift,:iso_level5_shift - Characters
-
:a,:b,:1,:space, etc.
Modifier Examples
-
:ctrl_c -
:alt_enter -
:shift_left -
:ctrl_alt_delete