Skip to content

Commit

Permalink
screen docs (#3955)
Browse files Browse the repository at this point in the history
* screen docs

* Added push_screen_wait

* words and test

* overload typing

* docstring

* style tweak

* ws

* Update docs/guide/screens.md

Co-authored-by: Rodrigo Girão Serrão <[email protected]>

* Update docs/guide/screens.md

Co-authored-by: Rodrigo Girão Serrão <[email protected]>

* Update src/textual/app.py

Co-authored-by: Rodrigo Girão Serrão <[email protected]>

* merge fix

* wording

* wording [skipci]

---------

Co-authored-by: Rodrigo Girão Serrão <[email protected]>
  • Loading branch information
willmcgugan and rodrigogiraoserrao authored Jan 4, 2024
1 parent babbc40 commit e5f2231
Show file tree
Hide file tree
Showing 7 changed files with 476 additions and 350 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed

- Breaking change: `Widget.move_child` parameters `before` and `after` are now keyword-only https://github.com/Textualize/textual/pull/3896
- Style tweak to toasts https://github.com/Textualize/textual/pull/3955

### Added

- Added textual.lazy https://github.com/Textualize/textual/pull/3936
- Added App.push_screen_wait https://github.com/Textualize/textual/pull/3955

## [0.46.0] - 2023-12-17

Expand Down
45 changes: 45 additions & 0 deletions docs/examples/guide/screens/questions01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from textual import on, work
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Button, Label


class QuestionScreen(Screen[bool]):
"""Screen with a parameter."""

def __init__(self, question: str) -> None:
self.question = question
super().__init__()

def compose(self) -> ComposeResult:
yield Label(self.question)
yield Button("Yes", id="yes", variant="success")
yield Button("No", id="no")

@on(Button.Pressed, "#yes")
def handle_yes(self) -> None:
self.dismiss(True) # (1)!

@on(Button.Pressed, "#no")
def handle_no(self) -> None:
self.dismiss(False) # (2)!


class QuestionsApp(App):
"""Demonstrates wait_for_dismiss"""

CSS_PATH = "questions01.tcss"

@work # (3)!
async def on_mount(self) -> None:
if await self.push_screen_wait( # (4)!
QuestionScreen("Do you like Textual?"),
):
self.notify("Good answer!")
else:
self.notify(":-(", severity="error")


if __name__ == "__main__":
app = QuestionsApp()
app.run()
17 changes: 17 additions & 0 deletions docs/examples/guide/screens/questions01.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
QuestionScreen {
layout: grid;
grid-size: 2 2;
align: center bottom;
}

QuestionScreen > Label {
margin: 1;
text-align: center;
column-span: 2;
width: 1fr;
}

QuestionScreen Button {
margin: 2;
width: 1fr;
}
39 changes: 39 additions & 0 deletions docs/guide/screens.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,45 @@ You may have noticed in the previous example that we changed the base class to `
The addition of `[bool]` adds typing information that tells the type checker to expect a boolean in the call to `dismiss`, and that any callback set in `push_screen` should also expect the same type. As always, typing is optional in Textual, but this may help you catch bugs.


### Waiting for screens

It is also possible to wait on a screen to be dismissed, which can feel like a more natural way of expressing logic than a callback.
The [`push_screen_wait()`][textual.app.App.push_screen_wait] method will push a screen and wait for its result (the value from [`Screen.dismiss()`][textual.screen.Screen.dismiss]).

This can only be done from a [worker](./workers.md), so that waiting for the screen doesn't prevent your app from updating.

Let's look at an example that uses `push_screen_wait` to ask a question and waits for the user to reply by clicking a button.


=== "questions01.py"

```python title="questions01.py" hl_lines="35-37"
--8<-- "docs/examples/guide/screens/questions01.py"
```

1. Dismiss with `True` when pressing the Yes button.
2. Dismiss with `False` when pressing the No button.
3. The `work` decorator will make this method run in a worker (background task).
4. Will return a result when the user clicks one of the buttons.


=== "questions01.tcss"

```css title="questions01.tcss"
--8<-- "docs/examples/guide/screens/questions01.tcss"
```

=== "Output"

```{.textual path="docs/examples/guide/screens/questions01.py"}
```

The mount handler on the app is decorated with `@work`, which makes the code run in a worker (background task).
In the mount handler we push the screen with the `push_screen_wait`.
When the user presses one of the buttons, the screen calls [`dismiss()`][textual.screen.Screen.dismiss] with either `True` or `False`.
This value is then returned from the `push_screen_wait` method in the mount handler.


## Modes

Some apps may benefit from having multiple screen stacks, rather than just one.
Expand Down
23 changes: 23 additions & 0 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,29 @@ def push_screen(
else:
return await_mount

@overload
async def push_screen_wait(
self, screen: Screen[ScreenResultType]
) -> ScreenResultType:
...

@overload
async def push_screen_wait(self, screen: str) -> Any:
...

async def push_screen_wait(
self, screen: Screen[ScreenResultType] | str
) -> ScreenResultType | Any:
"""Push a screen and wait for the result (received from [`Screen.dismiss`][textual.screen.Screen.dismiss]).
Args:
screen: A screen or the name of an installed screen.
Returns:
The screen's result.
"""
return await self.push_screen(screen, wait_for_dismiss=True)

def switch_screen(self, screen: Screen | str) -> AwaitMount:
"""Switch to another [screen](/guide/screens) by replacing the top of the screen stack with a new screen.
Expand Down
8 changes: 4 additions & 4 deletions src/textual/widgets/_toast.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,27 +58,27 @@ class Toast(Static, inherit_css=False):
}
Toast {
border-right: wide $background;
border-right: outer $background;
}
Toast.-information {
border-left: wide $success;
border-left: outer $success;
}
Toast.-information .toast--title {
color: $success-darken-1;
}
Toast.-warning {
border-left: wide $warning;
border-left: outer $warning;
}
Toast.-warning .toast--title {
color: $warning-darken-1;
}
Toast.-error {
border-left: wide $error;
border-left: outer $error;
}
Toast.-error .toast--title {
Expand Down
692 changes: 346 additions & 346 deletions tests/snapshot_tests/__snapshots__/test_snapshots.ambr

Large diffs are not rendered by default.

0 comments on commit e5f2231

Please sign in to comment.