# Textual Events and Messages Reference ## Overview Textual uses an event-driven architecture with two main types of communication: 1. **Events** - User interactions (keyboard, mouse) 2. **Messages** - Widget-to-widget communication ## Event Handler Patterns ### Basic Pattern ```python def on__(self, event: EventType) -> None: """Handle event""" ``` ### Examples ```python # Widget events def on_button_pressed(self, event: Button.Pressed) -> None: pass # DataTable events def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None: pass # Key events def on_key(self, event: events.Key) -> None: if event.key == "escape": self.exit() ``` ## Common Widget Events ### DataTable Events | Event | Handler | Description | |-------|---------|-------------| | `RowHighlighted` | `on_data_table_row_highlighted` | Cursor moves to row | | `RowSelected` | `on_data_table_row_selected` | Row selected (Enter) | | `CellHighlighted` | `on_data_table_cell_highlighted` | Cursor moves to cell | | `CellSelected` | `on_data_table_cell_selected` | Cell selected | | `ColumnHighlighted` | `on_data_table_column_highlighted` | Column highlighted | | `HeaderSelected` | `on_data_table_header_selected` | Header clicked | ### Button Events | Event | Handler | Description | |-------|---------|-------------| | `Pressed` | `on_button_pressed` | Button clicked/activated | ### Input Events | Event | Handler | Description | |-------|---------|-------------| | `Changed` | `on_input_changed` | Text changed | | `Submitted` | `on_input_submitted` | Enter pressed | ## Custom Messages ### Creating a Custom Message ```python from textual.message import Message class MyWidget(Widget): class DataUpdated(Message): """Custom message when data updates""" def __init__(self, data: dict) -> None: self.data = data super().__init__() def update_data(self, new_data: dict) -> None: # Post custom message self.post_message(self.DataUpdated(new_data)) ``` ### Handling Custom Messages ```python class MyApp(App): def on_my_widget_data_updated(self, event: MyWidget.DataUpdated) -> None: """Handle custom message""" self.process_data(event.data) ``` ## Event Properties ### Common Event Attributes ```python # All events have: event.stop() # Stop propagation event.prevent_default() # Prevent default behavior # Keyboard events event.key # Key name ("a", "ctrl+c", "escape") event.character # Unicode character event.aliases # List of key aliases # Mouse events event.x, event.y # Coordinates event.button # Mouse button number event.shift, event.ctrl, event.alt # Modifier keys # DataTable events event.row_key # Row identifier event.row_index # Row number event.coordinate # (row, column) tuple ``` ## Event Flow 1. **Capture Phase**: Event travels down from App to target 2. **Bubble Phase**: Event travels up from target to App ```python # Stop event propagation def on_key(self, event: events.Key) -> None: if event.key == "q": event.stop() # Don't let parent widgets see this ``` ## Message Posting ### Post to Self ```python self.post_message(MyMessage()) ``` ### Post to Parent ```python self.post_message_no_wait(MyMessage()) # Don't wait for processing ``` ### Post to Specific Widget ```python target_widget.post_message(MyMessage()) ``` ## Event Debugging ### Log All Events ```python def on_event(self, event: events.Event) -> None: """Log all events for debugging""" self.log(f"Event: {event.__class__.__name__}") ``` ### Check Handler Names ```python # List all event handlers handlers = [m for m in dir(self) if m.startswith('on_')] ``` ## Common Patterns ### Debouncing Events ```python from textual.timer import Timer class MyWidget(Widget): def __init__(self): self._update_timer: Timer | None = None def on_input_changed(self, event: Input.Changed) -> None: # Cancel previous timer if self._update_timer: self._update_timer.cancel() # Set new timer self._update_timer = self.set_timer(0.3, self.perform_update) ``` ### Event Validation ```python def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None: # Validate event data if not event.row_key: return # Convert types if needed row_key_str = str(event.row_key) ``` ## Common Mistakes 1. **Wrong handler name**: `on_data_table_cursor_moved` ❌ → `on_data_table_row_highlighted` ✅ 2. **Missing namespace**: `on_row_selected` ❌ → `on_data_table_row_selected` ✅ 3. **Wrong event class**: `DataTable.CursorMoved` ❌ → `DataTable.RowHighlighted` ✅ 4. **Not converting types**: Assuming `row_key` is string when it's `RowKey` type