Skip to content

Commit 881a9c2

Browse files
authored
Merge pull request #1061 from Textualize/horizontal-fix
allow waiting of screen operations
2 parents c0c03e8 + 170be7e commit 881a9c2

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
@@ -861,8 +861,6 @@ def is_screen_installed(self, screen: Screen | str) -> bool:
861861
def get_screen(self, screen: Screen | str) -> Screen:
862862
"""Get an installed screen.
863863
864-
If the screen isn't running, it will be registered before it is run.
865-
866864
Args:
867865
screen (Screen | str): Either a Screen object or screen name (the `name` argument when installed).
868866
@@ -879,10 +877,30 @@ def get_screen(self, screen: Screen | str) -> Screen:
879877
raise KeyError(f"No screen called {screen!r} installed") from None
880878
else:
881879
next_screen = screen
882-
if not next_screen.is_running:
883-
self._register(self, next_screen)
884880
return next_screen
885881

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

903-
def push_screen(self, screen: Screen | str) -> None:
921+
def push_screen(self, screen: Screen | str) -> AwaitMount:
904922
"""Push a new screen on the screen stack.
905923
906924
Args:
907925
screen (Screen | str): A Screen instance or the name of an installed screen.
908926
909927
"""
910-
next_screen = self.get_screen(screen)
928+
next_screen, await_mount = self._get_screen(screen)
911929
self._screen_stack.append(next_screen)
912930
self.screen.post_message_no_wait(events.ScreenResume(self))
913931
self.log.system(f"{self.screen} is current (PUSHED)")
932+
return await_mount
914933

915-
def switch_screen(self, screen: Screen | str) -> None:
934+
def switch_screen(self, screen: Screen | str) -> AwaitMount:
916935
"""Switch to another screen by replacing the top of the screen stack with a new screen.
917936
918937
Args:
@@ -921,12 +940,14 @@ def switch_screen(self, screen: Screen | str) -> None:
921940
"""
922941
if self.screen is not screen:
923942
self._replace_screen(self._screen_stack.pop())
924-
next_screen = self.get_screen(screen)
943+
next_screen, await_mount = self._get_screen(screen)
925944
self._screen_stack.append(next_screen)
926945
self.screen.post_message_no_wait(events.ScreenResume(self))
927946
self.log.system(f"{self.screen} is current (SWITCHED)")
947+
return await_mount
948+
return AwaitMount([])
928949

929-
def install_screen(self, screen: Screen, name: str | None = None) -> str:
950+
def install_screen(self, screen: Screen, name: str | None = None) -> AwaitMount:
930951
"""Install a screen.
931952
932953
Args:
@@ -938,7 +959,7 @@ def install_screen(self, screen: Screen, name: str | None = None) -> str:
938959
ScreenError: If the screen can't be installed.
939960
940961
Returns:
941-
str: The name of the screen
962+
AwaitMount: An awaitable that awaits the mounting of the screen and its children.
942963
"""
943964
if name is None:
944965
name = nanoid.generate()
@@ -949,9 +970,9 @@ def install_screen(self, screen: Screen, name: str | None = None) -> str:
949970
"Can't install screen; {screen!r} has already been installed"
950971
)
951972
self._installed_screens[name] = screen
952-
self.get_screen(name) # Ensures screen is running
973+
_screen, await_mount = self._get_screen(name) # Ensures screen is running
953974
self.log.system(f"{screen} INSTALLED name={name!r}")
954-
return name
975+
return await_mount
955976

956977
def uninstall_screen(self, screen: Screen | str) -> str | None:
957978
"""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)