or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

content.mdcore-framework.mdevents.mdindex.mdstyling.mdtesting.mdwidgets.md

events.mddocs/

0

# Event System

1

2

Comprehensive event handling system for user interactions, widget lifecycle, and system events, including keyboard input, mouse interactions, focus management, and custom message passing.

3

4

## Capabilities

5

6

### Base Event Classes

7

8

Foundation classes for all events and messages in the Textual framework.

9

10

```python { .api }

11

class Message:

12

"""Base class for all messages."""

13

14

def __init__(self) -> None:

15

"""Initialize a message."""

16

17

def prevent_default(self) -> None:

18

"""Prevent the default handler for this message."""

19

20

def stop(self) -> None:

21

"""Stop message propagation."""

22

23

# Properties

24

sender: MessageTarget | None

25

time: float

26

handler_name: str | None

27

28

class Event(Message):

29

"""Base class for all events."""

30

31

def __init__(self) -> None:

32

"""Initialize an event."""

33

34

# Properties

35

bubble: bool # Whether the event bubbles up the DOM

36

verbose: bool # Whether to include in verbose logging

37

38

class InputEvent(Event):

39

"""Base class for input-related events."""

40

pass

41

```

42

43

### Keyboard Events

44

45

Events for handling keyboard input and key combinations.

46

47

```python { .api }

48

class Key(InputEvent):

49

"""Keyboard key press event."""

50

51

def __init__(self, key: str, character: str | None = None):

52

"""

53

Initialize a key event.

54

55

Parameters:

56

- key: The key identifier (e.g., "enter", "escape", "ctrl+c")

57

- character: The character representation (if printable)

58

"""

59

60

@property

61

def is_printable(self) -> bool:

62

"""Whether the key produces a printable character."""

63

64

# Properties

65

key: str # Key identifier

66

character: str | None # Character representation

67

name: str # Human-readable key name

68

aliases: list[str] # Alternative key names

69

```

70

71

### Mouse Events

72

73

Events for handling mouse interactions including clicks, movement, and scrolling.

74

75

```python { .api }

76

class MouseEvent(InputEvent):

77

"""Base class for mouse events."""

78

79

def __init__(self, x: int, y: int, button: int = 0):

80

"""

81

Initialize a mouse event.

82

83

Parameters:

84

- x: Mouse X coordinate

85

- y: Mouse Y coordinate

86

- button: Mouse button number

87

"""

88

89

# Properties

90

x: int # Mouse X coordinate

91

y: int # Mouse Y coordinate

92

button: int # Mouse button (0=left, 1=middle, 2=right)

93

screen_x: int # Screen-relative X

94

screen_y: int # Screen-relative Y

95

96

class MouseDown(MouseEvent):

97

"""Mouse button pressed down event."""

98

pass

99

100

class MouseUp(MouseEvent):

101

"""Mouse button released event."""

102

pass

103

104

class Click(MouseEvent):

105

"""Mouse click event (down + up)."""

106

pass

107

108

class MouseMove(MouseEvent):

109

"""Mouse movement event."""

110

pass

111

112

class MouseScrollDown(MouseEvent):

113

"""Mouse scroll down event."""

114

pass

115

116

class MouseScrollUp(MouseEvent):

117

"""Mouse scroll up event."""

118

pass

119

```

120

121

### Focus and Visibility Events

122

123

Events related to widget focus state and visibility changes.

124

125

```python { .api }

126

class Focus(Event):

127

"""Widget gained focus event."""

128

129

def __init__(self, widget: Widget):

130

"""

131

Initialize a focus event.

132

133

Parameters:

134

- widget: The widget that gained focus

135

"""

136

137

# Properties

138

widget: Widget

139

140

class Blur(Event):

141

"""Widget lost focus event."""

142

143

def __init__(self, widget: Widget):

144

"""

145

Initialize a blur event.

146

147

Parameters:

148

- widget: The widget that lost focus

149

"""

150

151

# Properties

152

widget: Widget

153

154

class Show(Event):

155

"""Widget became visible event."""

156

157

def __init__(self, widget: Widget):

158

"""

159

Initialize a show event.

160

161

Parameters:

162

- widget: The widget that became visible

163

"""

164

165

class Hide(Event):

166

"""Widget became hidden event."""

167

168

def __init__(self, widget: Widget):

169

"""

170

Initialize a hide event.

171

172

Parameters:

173

- widget: The widget that became hidden

174

"""

175

```

176

177

### Widget Lifecycle Events

178

179

Events that occur during widget creation, mounting, and destruction.

180

181

```python { .api }

182

class Mount(Event):

183

"""Widget was mounted to the DOM event."""

184

185

def __init__(self, widget: Widget):

186

"""

187

Initialize a mount event.

188

189

Parameters:

190

- widget: The widget that was mounted

191

"""

192

193

class Unmount(Event):

194

"""Widget was unmounted from the DOM event."""

195

196

def __init__(self, widget: Widget):

197

"""

198

Initialize an unmount event.

199

200

Parameters:

201

- widget: The widget that was unmounted

202

"""

203

204

class Compose(Event):

205

"""Widget composition completed event."""

206

pass

207

208

class Ready(Event):

209

"""Application is ready to receive events."""

210

pass

211

212

class Load(Event):

213

"""Application/widget loading completed event."""

214

pass

215

```

216

217

### System and Layout Events

218

219

Events related to system state and layout changes.

220

221

```python { .api }

222

class Resize(Event):

223

"""Screen or widget resize event."""

224

225

def __init__(self, size: Size, virtual_size: Size):

226

"""

227

Initialize a resize event.

228

229

Parameters:

230

- size: New size of the widget/screen

231

- virtual_size: Virtual size including scrollable area

232

"""

233

234

# Properties

235

size: Size

236

virtual_size: Size

237

238

class Idle(Event):

239

"""Application idle event (no recent input)."""

240

pass

241

242

class ExitApp(Event):

243

"""Application exit requested event."""

244

245

def __init__(self, result: Any = None):

246

"""

247

Initialize an exit event.

248

249

Parameters:

250

- result: Exit result value

251

"""

252

253

# Properties

254

result: Any

255

256

class Callback(Event):

257

"""Callback function invocation event."""

258

259

def __init__(self, callback: Callable, *args, **kwargs):

260

"""

261

Initialize a callback event.

262

263

Parameters:

264

- callback: Function to call

265

- *args: Positional arguments

266

- **kwargs: Keyword arguments

267

"""

268

269

class Timer(Event):

270

"""Timer callback event."""

271

272

def __init__(self, timer: Timer, time: float):

273

"""

274

Initialize a timer event.

275

276

Parameters:

277

- timer: The timer instance

278

- time: Current time

279

"""

280

```

281

282

### Custom Widget Events

283

284

Common event patterns used by built-in widgets.

285

286

```python { .api }

287

class Changed(Event):

288

"""Generic value changed event."""

289

290

def __init__(self, widget: Widget, value: Any):

291

"""

292

Initialize a changed event.

293

294

Parameters:

295

- widget: Widget that changed

296

- value: New value

297

"""

298

299

# Properties

300

widget: Widget

301

value: Any

302

303

class Pressed(Event):

304

"""Generic button/item pressed event."""

305

306

def __init__(self, widget: Widget):

307

"""

308

Initialize a pressed event.

309

310

Parameters:

311

- widget: Widget that was pressed

312

"""

313

314

class Selected(Event):

315

"""Generic selection event."""

316

317

def __init__(self, widget: Widget, item: Any):

318

"""

319

Initialize a selected event.

320

321

Parameters:

322

- widget: Widget with selection

323

- item: Selected item

324

"""

325

326

class Toggled(Event):

327

"""Generic toggle state changed event."""

328

329

def __init__(self, widget: Widget, toggled: bool):

330

"""

331

Initialize a toggled event.

332

333

Parameters:

334

- widget: Widget that was toggled

335

- toggled: New toggle state

336

"""

337

```

338

339

## Usage Examples

340

341

### Basic Event Handling

342

343

```python

344

from textual.app import App

345

from textual.widgets import Button, Input

346

from textual.events import Key

347

348

class EventApp(App):

349

def compose(self):

350

yield Input(placeholder="Type here...", id="input")

351

yield Button("Click me!", id="button")

352

353

def on_key(self, event: Key):

354

"""Handle any key press."""

355

self.log(f"Key pressed: {event.key}")

356

357

if event.key == "ctrl+q":

358

self.exit()

359

360

def on_button_pressed(self, event: Button.Pressed):

361

"""Handle button presses."""

362

self.log(f"Button {event.button.id} was pressed!")

363

364

def on_input_changed(self, event: Input.Changed):

365

"""Handle input value changes."""

366

self.log(f"Input value: {event.value}")

367

368

def on_input_submitted(self, event: Input.Submitted):

369

"""Handle input submission (Enter key)."""

370

self.log(f"Input submitted: {event.value}")

371

```

372

373

### Event Handling with Decorators

374

375

```python

376

from textual.app import App

377

from textual.widgets import Button, Input

378

from textual import on

379

380

class DecoratorApp(App):

381

def compose(self):

382

yield Input(id="name-input")

383

yield Button("Submit", id="submit-btn")

384

yield Button("Clear", id="clear-btn")

385

386

@on(Button.Pressed, "#submit-btn")

387

def handle_submit(self, event: Button.Pressed):

388

"""Handle submit button with CSS selector."""

389

input_widget = self.query_one("#name-input", Input)

390

self.log(f"Submitting: {input_widget.value}")

391

392

@on(Button.Pressed, "#clear-btn")

393

def handle_clear(self, event: Button.Pressed):

394

"""Handle clear button."""

395

input_widget = self.query_one("#name-input", Input)

396

input_widget.value = ""

397

398

@on(Input.Changed, "#name-input")

399

def handle_name_change(self, event: Input.Changed):

400

"""Handle name input changes."""

401

submit_btn = self.query_one("#submit-btn", Button)

402

submit_btn.disabled = len(event.value.strip()) == 0

403

```

404

405

### Custom Event Creation

406

407

```python

408

from textual.app import App

409

from textual.widget import Widget

410

from textual.message import Message

411

from textual.widgets import Button

412

413

class CustomWidget(Widget):

414

"""A widget that sends custom events."""

415

416

class DataReady(Message):

417

"""Custom event sent when data is ready."""

418

419

def __init__(self, widget: Widget, data: dict):

420

super().__init__()

421

self.widget = widget

422

self.data = data

423

424

def compose(self):

425

yield Button("Load Data", id="load")

426

427

def on_button_pressed(self, event: Button.Pressed):

428

"""Handle button press and send custom event."""

429

if event.button.id == "load":

430

# Simulate data loading

431

data = {"result": "success", "items": [1, 2, 3]}

432

433

# Send custom event

434

self.post_message(self.DataReady(self, data))

435

436

class CustomEventApp(App):

437

def compose(self):

438

yield CustomWidget(id="custom")

439

440

def on_custom_widget_data_ready(self, event: CustomWidget.DataReady):

441

"""Handle custom data ready event."""

442

self.log(f"Data received: {event.data}")

443

```

444

445

### Mouse Event Handling

446

447

```python

448

from textual.app import App

449

from textual.widgets import Static

450

from textual.events import MouseDown, MouseUp, MouseMove

451

452

class MouseApp(App):

453

def compose(self):

454

yield Static("Click and drag on me!", id="target")

455

456

def on_mouse_down(self, event: MouseDown):

457

"""Handle mouse button press."""

458

self.log(f"Mouse down at ({event.x}, {event.y}), button {event.button}")

459

460

def on_mouse_up(self, event: MouseUp):

461

"""Handle mouse button release."""

462

self.log(f"Mouse up at ({event.x}, {event.y})")

463

464

def on_mouse_move(self, event: MouseMove):

465

"""Handle mouse movement."""

466

# Only log occasionally to avoid spam

467

if event.x % 5 == 0 and event.y % 5 == 0:

468

self.log(f"Mouse at ({event.x}, {event.y})")

469

```

470

471

### Event Prevention and Stopping

472

473

```python

474

from textual.app import App

475

from textual.widgets import Input

476

from textual.events import Key

477

478

class PreventionApp(App):

479

def compose(self):

480

yield Input(placeholder="Try typing numbers...", id="text-only")

481

482

def on_key(self, event: Key):

483

"""Prevent numeric input in text field."""

484

# Check if the focused widget is our text-only input

485

focused = self.focused

486

if focused and focused.id == "text-only":

487

# Prevent numeric characters

488

if event.character and event.character.isdigit():

489

event.prevent_default() # Stop default handling

490

event.stop() # Stop event propagation

491

self.log("Numeric input blocked!")

492

```