Skip to content

Commit ca8eb3d

Browse files
davepwillmcgugan
andauthored
Notifications (#2866)
* Add a notification class and a class to hold notifications This provides the core classes for holding information on a single notification, and then on top of that a class for managing a collection of notifications. * WiP: End of day/week commit to pick up post-holiday * Ask permission rather than forgiveness Yes, this does go against all things Pythonic, but in this case it's likely less costly to do the check first; moreover it works around the problem I ran in to: #2863 * Move the handling of "I've seen this" into the toast rack This way the interface becomes "here's a bunch of notifications, you go work this out". * Add a notify method to all widgets * The removal time for a toast should be the time left When it was per-screen, it made sense that it was the timeout; now that we're carrying them over between screens we're going to make sure they're only around for as long as they need to be. * Carry notifications between screens * Remove the test code * Drop the borders from the toasts Except for the title, keep that. * Provide access to the notification timeout * Remove the title panel from a Toast if the title is empty * Make the Toast CSS classes "private" Prefix with a - to reduce the chance of a clash with userspace. * Refresh a docstring * Stop widget leakage The Toasts were removing themselves, but they're wrapped inside a helper container that keeps them aligned right. So the problem was that the alignment containers were leaking. This ensures that when a Toast goes away it takes its parent with it. * Make the alignment container hidden This doesn't really make any difference, but it feels like it makes sense to hide it if there's nothing to show -- it's purely for alignment. * 🚚 Rename the toast container This is about getting the toasts to align correctly (even when you do align things, they don't really align as expected due to the way that a container aligns the bounding box of all if its children, not the individual children). However, I had this named after where it aligned them to; someone using the system may wish to change that, so let's make the name more generic. * Improve ToastRack._toast_id Add a docstring, and also change the format of the identity somewhat so that it's even "more internal". * Add some initial low-level notification testing * Add initial testing of notifications within an application * Add tests for notifying from the 3 main levels within the DOM * Add a toast example to the docs and a snapshot test This might not be the final form, but it'll do for the moment. I want to get the snapshot test in place at least. * Add a snapshot test for notifications persisting between screens * Add some documentation for a Toast This isn't going into the index, just yet. This is *technically* an internal widget so I'm not sure how and where it makes sense to document it; if at all. But let's get some documentation in here anyway. * Flesh out the docstrings for the notify methods * Add a missing docstring to the Notifications __init__ method * Add snapshot tests for persisting notifications through mode switches * Remove unused import Looks like eglot/pyright tried to be "helpful" at some point and I didn't notice. * Correct the Toast severity level classes in the docs Originally they weren't in the "internal" namespace, then I decided that they should be so there's less chance of a clash with dev-space code; but I forgot to reflect this in the docs. This fixes that. * Make the removal of notifications/toasts a two way thing The addition of the ability to dismiss a toast by clicking on it had a flaw: the notification->toast code had been written with things being one way. The expiration of notifications happened in the notification handler, and the expiration of Toasts was done in the Toast system, on purpose (notifications might end up being routed via elsewhere so this needs to be done). But... this meant that hand-removed Toasts kept coming back from the dead when a new notification was raised iff the hand-removed ones hadn't yet expired. So here I add the ability the remove a notification from the notification collection. * Remove an unhelpful comment Sort of a hangover from what was initially looking like it was going to be a longer body of code. It doesn't really need explaining any more. * Add in support to the notification collection * Change the toast rack adder to be a general "show" method This turns the method into one that further aids making the connection between the notifications and the toasts two way. Now it makes sense that if there are toasts for notifications that no longer exist, they also get removed. This makes it easier to add all sorts of clear options later on. * Add a method to clear notifications * Add an App method for clearing all existing notifications * Add a missing docstring to _refresh_notifications * Return the notification from the notify methods It can be seen as, and used as, a handle of sorts (see unnotify); so return it so people can use it. * Add some more notifications unit testing * Add some more app-level notification unit testing * Style tweaks * docs * added notifications * snapshots --------- Co-authored-by: Will McGugan <[email protected]>
1 parent 4d699c8 commit ca8eb3d

File tree

17 files changed

+1418
-31
lines changed

17 files changed

+1418
-31
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Added `DataTable.remove_column` method https://github.com/Textualize/textual/pull/2899
13+
- Added notifications https://github.com/Textualize/textual/pull/2866
1314

1415
### Fixed
1516

@@ -26,9 +27,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2627
### Added
2728

2829
- Updated `DataTable.get_cell` type hints to accept string keys https://github.com/Textualize/textual/issues/2586
29-
- Added `DataTable.get_cell_coordinate` method
30+
- Added `DataTable.get_cell_coordinate` method
3031
- Added `DataTable.get_row_index` method https://github.com/Textualize/textual/issues/2587
31-
- Added `DataTable.get_column_index` method
32+
- Added `DataTable.get_column_index` method
3233
- Added can-focus pseudo-class to target widgets that may receive focus
3334
- Make `Markdown.update` optionally awaitable https://github.com/Textualize/textual/pull/2838
3435
- Added `default` parameter to `DataTable.add_column` for populating existing rows https://github.com/Textualize/textual/pull/2836

docs/examples/widgets/toast.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from textual.app import App
2+
3+
4+
class ToastApp(App[None]):
5+
def on_mount(self) -> None:
6+
# Show an information notification.
7+
self.notify("It's an older code, sir, but it checks out.")
8+
9+
# Show a warning. Note that Textual's notification system allows
10+
# for the use of Rich console markup.
11+
self.notify(
12+
"Now witness the firepower of this fully "
13+
"[b]ARMED[/b] and [i][b]OPERATIONAL[/b][/i] battle station!",
14+
title="Possible trap detected",
15+
severity="warning",
16+
)
17+
18+
# Show an error. Set a longer timeout so it's noticed.
19+
self.notify("It's a trap!", severity="error", timeout=10)
20+
21+
# Show an information notification, but without any sort of title.
22+
self.notify("It's against my programming to impersonate a deity.", title="")
23+
24+
25+
if __name__ == "__main__":
26+
ToastApp().run()

docs/widgets/toast.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Toast
2+
3+
!!! tip "Added in version 0.30.0"
4+
5+
A widget which displays a notification message.
6+
7+
- [ ] Focusable
8+
- [ ] Container
9+
10+
Note that `Toast` isn't designed to be used directly in your applications,
11+
but it is instead used by [`notify`][textual.app.App.notify] to
12+
display a message when using Textual's built-in notification system.
13+
14+
## Styling
15+
16+
You can customize the style of Toasts by targeting the `Toast` [CSS type](/guide/CSS/#type-selector).
17+
For example:
18+
19+
20+
```scss
21+
Toast {
22+
padding: 3;
23+
}
24+
```
25+
26+
The three severity levels also have corresponding
27+
[classes](/guide/CSS/#class-name-selector), allowing you to target the
28+
different styles of notification. They are:
29+
30+
- `-information`
31+
- `-warning`
32+
- `-error`
33+
34+
If you wish to tailor the notifications for your application you can add
35+
rules to your CSS like this:
36+
37+
```scss
38+
Toast.-information {
39+
/* Styling here. */
40+
}
41+
42+
Toast.-warning {
43+
/* Styling here. */
44+
}
45+
46+
Toast.-error {
47+
/* Styling here. */
48+
}
49+
```
50+
51+
You can customize just the title wih the `toast--title` class.
52+
The following would make the title italic for an information toast:
53+
54+
```scss
55+
Toast.-information .toast--title {
56+
text-style: italic;
57+
}
58+
59+
```
60+
61+
## Example
62+
63+
=== "Output"
64+
65+
```{.textual path="docs/examples/widgets/toast.py"}
66+
```
67+
68+
=== "toast.py"
69+
70+
```python
71+
--8<-- "docs/examples/widgets/toast.py"
72+
```
73+
74+
---
75+
76+
::: textual.widgets._toast
77+
options:
78+
show_root_heading: true
79+
show_root_toc_entry: true

0 commit comments

Comments
 (0)