Skip to content

Commit 170be7e

Browse files
committed
allow waiting of screen operations
1 parent cfd5d53 commit 170be7e

File tree

5 files changed

+49
-22
lines changed

5 files changed

+49
-22
lines changed

src/textual/app.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -860,8 +860,6 @@ def is_screen_installed(self, screen: Screen | str) -> bool:
860860
def get_screen(self, screen: Screen | str) -> Screen:
861861
"""Get an installed screen.
862862
863-
If the screen isn't running, it will be registered before it is run.
864-
865863
Args:
866864
screen (Screen | str): Either a Screen object or screen name (the `name` argument when installed).
867865
@@ -878,10 +876,30 @@ def get_screen(self, screen: Screen | str) -> Screen:
878876
raise KeyError(f"No screen called {screen!r} installed") from None
879877
else:
880878
next_screen = screen
881-
if not next_screen.is_running:
882-
self._register(self, next_screen)
883879
return next_screen
884880

881+
def _get_screen(self, screen: Screen | str) -> tuple[Screen, AwaitMount]:
882+
"""Get an installed screen and a await mount object.
883+
884+
If the screen isn't running, it will be registered before it is run.
885+
886+
Args:
887+
screen (Screen | str): Either a Screen object or screen name (the `name` argument when installed).
888+
889+
Raises:
890+
KeyError: If the named screen doesn't exist.
891+
892+
Returns:
893+
tuple[Screen, AwaitMount]: A screen instance and an awaitable that awaits the children mounting.
894+
895+
"""
896+
_screen = self.get_screen(screen)
897+
if not _screen.is_running:
898+
widgets = self._register(self, _screen)
899+
return (_screen, AwaitMount(widgets))
900+
else:
901+
return (_screen, AwaitMount([]))
902+
885903
def _replace_screen(self, screen: Screen) -> Screen:
886904
"""Handle the replaced screen.
887905
@@ -899,19 +917,20 @@ def _replace_screen(self, screen: Screen) -> Screen:
899917
self.log.system(f"{screen} REMOVED")
900918
return screen
901919

902-
def push_screen(self, screen: Screen | str) -> None:
920+
def push_screen(self, screen: Screen | str) -> AwaitMount:
903921
"""Push a new screen on the screen stack.
904922
905923
Args:
906924
screen (Screen | str): A Screen instance or the name of an installed screen.
907925
908926
"""
909-
next_screen = self.get_screen(screen)
927+
next_screen, await_mount = self._get_screen(screen)
910928
self._screen_stack.append(next_screen)
911929
self.screen.post_message_no_wait(events.ScreenResume(self))
912930
self.log.system(f"{self.screen} is current (PUSHED)")
931+
return await_mount
913932

914-
def switch_screen(self, screen: Screen | str) -> None:
933+
def switch_screen(self, screen: Screen | str) -> AwaitMount:
915934
"""Switch to another screen by replacing the top of the screen stack with a new screen.
916935
917936
Args:
@@ -920,12 +939,14 @@ def switch_screen(self, screen: Screen | str) -> None:
920939
"""
921940
if self.screen is not screen:
922941
self._replace_screen(self._screen_stack.pop())
923-
next_screen = self.get_screen(screen)
942+
next_screen, await_mount = self._get_screen(screen)
924943
self._screen_stack.append(next_screen)
925944
self.screen.post_message_no_wait(events.ScreenResume(self))
926945
self.log.system(f"{self.screen} is current (SWITCHED)")
946+
return await_mount
947+
return AwaitMount([])
927948

928-
def install_screen(self, screen: Screen, name: str | None = None) -> str:
949+
def install_screen(self, screen: Screen, name: str | None = None) -> AwaitMount:
929950
"""Install a screen.
930951
931952
Args:
@@ -937,7 +958,7 @@ def install_screen(self, screen: Screen, name: str | None = None) -> str:
937958
ScreenError: If the screen can't be installed.
938959
939960
Returns:
940-
str: The name of the screen
961+
AwaitMount: An awaitable that awaits the mounting of the screen and its children.
941962
"""
942963
if name is None:
943964
name = nanoid.generate()
@@ -948,9 +969,9 @@ def install_screen(self, screen: Screen, name: str | None = None) -> str:
948969
"Can't install screen; {screen!r} has already been installed"
949970
)
950971
self._installed_screens[name] = screen
951-
self.get_screen(name) # Ensures screen is running
972+
_screen, await_mount = self._get_screen(name) # Ensures screen is running
952973
self.log.system(f"{screen} INSTALLED name={name!r}")
953-
return name
974+
return await_mount
954975

955976
def uninstall_screen(self, screen: Screen | str) -> str | None:
956977
"""Uninstall a screen. If the screen was not previously installed then this

src/textual/demo.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,14 @@ DarkSwitch {
114114
}
115115

116116
DarkSwitch .label {
117-
117+
width: 1fr;
118118
padding: 1 2;
119119
color: $text-muted;
120120
}
121121

122122
DarkSwitch Checkbox {
123123
background: $boost;
124+
dock: left;
124125
}
125126

126127

src/textual/screen.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from ._compositor import Compositor, MapGeometry
1313
from .timer import Timer
1414
from ._types import CallbackType
15-
from .dom import DOMNode
1615
from .geometry import Offset, Region, Size
1716
from .reactive import Reactive
1817
from .renderables.blank import Blank
@@ -61,7 +60,12 @@ def is_transparent(self) -> bool:
6160
@property
6261
def is_current(self) -> bool:
6362
"""Check if this screen is current (i.e. visible to user)."""
64-
return self.app.screen is self
63+
from .app import ScreenStackError
64+
65+
try:
66+
return self.app.screen is self
67+
except ScreenStackError:
68+
return False
6569

6670
@property
6771
def update_timer(self) -> Timer:

src/textual/widget.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ def __init__(self, widgets: Sequence[Widget]) -> None:
8282

8383
def __await__(self) -> Generator[None, None, None]:
8484
async def await_mount() -> None:
85-
aws = [
86-
create_task(widget._mounted_event.wait()) for widget in self._widgets
87-
]
88-
if aws:
89-
await wait(aws)
85+
if self._widgets:
86+
aws = [
87+
create_task(widget._mounted_event.wait())
88+
for widget in self._widgets
89+
]
90+
if aws:
91+
await wait(aws)
9092

9193
return await_mount().__await__()
9294

tests/test_unmount.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ def on_unmount(self, event: events.Unmount):
2929

3030
class UnmountApp(App):
3131
async def on_mount(self) -> None:
32-
self.push_screen(MyScreen(id="main"))
32+
await self.push_screen(MyScreen(id="main"))
3333

3434
app = UnmountApp()
3535
async with app.run_test() as pilot:
36-
await pilot.pause() # TODO remove when push_screen is awaitable
3736
await pilot.exit(None)
3837

3938
expected = [

0 commit comments

Comments
 (0)