Extend @on decorator to filter matchable attributes (#2498)

* Add tests for #2484.

* Implement @on extension.

[skip ci]
Related issues: #2484.

* Changelog.

* Add missing @on test.

* Remove debug prints.

* Document changes.

* Update tests.

Test now fully works, as of #2490.

* Cache parsed selectors.

* Streamline exit condition.

* Fix typing.

* More succint wording.

* Document 'on' kwargs.

* Update src/textual/_on.py

Co-authored-by: Will McGugan <willmcgugan@gmail.com>

* Update docs/guide/events.md

Co-authored-by: Will McGugan <willmcgugan@gmail.com>

* Change 'on' API.

* Remove example code.

* Address feedback.

* Update src/textual/_on.py

Co-authored-by: Will McGugan <willmcgugan@gmail.com>

* Address review feedback.

* Fix #2499.

* don't require control to be manually specified

* update docstring

* deleted words

---------

Co-authored-by: Will McGugan <willmcgugan@gmail.com>
This commit is contained in:
Rodrigo Girão Serrão
2023-05-08 17:30:07 +01:00
committed by GitHub
parent dd7e768887
commit 855c90d4f0
10 changed files with 155 additions and 25 deletions

View File

@@ -218,10 +218,23 @@ Messages from builtin controls will have this attribute, but you may need to add
!!! note
If multiple decorated handlers match the `control`, then they will *all* be called in the order they are defined.
If multiple decorated handlers match the message, then they will *all* be called in the order they are defined.
The naming convention handler will be called *after* any decorated handlers.
#### Applying CSS selectors to arbitrary attributes
The `on` decorator also accepts selectors as keyword arguments that may be used to match other attributes in a Message, provided those attributes are in [`Message.ALLOW_SELECTOR_MATCH`][textual.message.Message.ALLOW_SELECTOR_MATCH].
The snippet below shows how to match the message [`TabbedContent.TabActivated`][textual.widgets.TabbedContent.TabActivated] only when the tab with id `home` was activated:
```py
@on(TabbedContent.TabActivated, tab="#home")
def home_tab(self) -> None:
self.log("Switched back to home tab.")
...
```
### Handler arguments
Message handler methods can be written with or without a positional argument. If you add a positional argument, Textual will call the handler with the event object. The following handler (taken from `custom01.py` above) contains a `message` parameter. The body of the code makes use of the message to set a preset color.
@@ -231,6 +244,14 @@ Message handler methods can be written with or without a positional argument. If
self.screen.styles.animate("background", message.color, duration=0.5)
```
A similar handler can be written using the decorator `on`:
```python
@on(ColorButton.Selected)
def animate_background_color(self, message: ColorButton.Selected) -> None:
self.screen.styles.animate("background", message.color, duration=0.5)
```
If the body of your handler doesn't require any information in the message you can omit it from the method signature. If we just want to play a bell noise when the button is clicked, we could write our handler like this:
```python