Skip to content

docs: clarify key_* methods vs on_key for App-level key handling#6619

Open
twall wants to merge 1 commit into
Textualize:mainfrom
twall:docs/key-method-app-level-handling
Open

docs: clarify key_* methods vs on_key for App-level key handling#6619
twall wants to merge 1 commit into
Textualize:mainfrom
twall:docs/key-method-app-level-handling

Conversation

@twall

@twall twall commented Jul 1, 2026

Copy link
Copy Markdown

Problem

The docs describe key_* methods as "a convenience for experimenting" and say bindings/actions are "nearly always preferable." This misses a critical use case and leaves developers confused.

on_key on App does not receive key events that were stopped by a focused widget (e.g. Input). This is because on_key relies on event bubbling, and Input calls event.stop() on keys it handles.

key_* methods on App do fire regardless — they're called directly by App._on_key via dispatch_key(), before bubbling. This makes them the correct tool for App-level keys that must work regardless of what widget has focus.

This distinction is non-obvious and took significant debugging to discover. Neither input.md nor events.md mentioned it.

Changes

  • input.md: Expands the key methods note with the practical implication and a concrete example showing the isinstance(self.focused, Input) guard pattern.
  • events.md: Adds a tip in the "Stopping bubbling" section cross-referencing key_* methods as the solution.

Example

class MyApp(App):
    # ❌ Won't fire when Input has focus — Input stops the event
    def on_key(self, event: Key) -> None:
        if event.key == "enter":
            self._handle_enter()

    # ✅ Fires regardless of which widget has focus
    def key_enter(self, event: Key) -> None:
        if not isinstance(self.focused, Input):
            self._handle_enter()

key_* methods on App are called directly by the internal key dispatcher
regardless of whether a child widget stopped the event. on_key handlers
on App do NOT receive keys that were stopped by a focused widget (e.g.
Input). This distinction is important and was previously undocumented.

- input.md: expand the key methods note with the practical implication
  and a concrete example
- events.md: add a tip in 'Stopping bubbling' cross-referencing key_*
  methods as the solution when Input consumes keys
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant