# 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_<namespace>_<event_name>(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