-
-
Notifications
You must be signed in to change notification settings - Fork 999
Description
I just want to pop a couple of screens (more than one, let's say 2), nothing else.
Now this is a painful experience because there is no public API allowing for that.
It appears to be as simple as:
while not isinstance(self.app.screen, SomeScreen)
self.app.pop_screen()
but that's not true, because with every pop_screen, there is ScreenResume message being posted which might result in bugs when something depends on that. It also causes old screens to appear for a moment bug I think it's a regression.
I know about the MODES and they can be useful in some scenarios.
But not everything can be generalized to a mode, and even within a given mode, there may be a need to pop several screens.
This is what I do to achieve it and it is still not ideal because of the regression of the flicker effect:
def pop_screen_until(self, *screens: str | type[Screen[ScreenResultType]]) -> AwaitComplete:
"""
Pop all screens until one of the given screen is on top of the stack.
Raises
------
ScreenNotFoundError: if no screen was found.
"""
async def _pop_screen_until() -> None:
for screen in screens:
if not self._is_screen_in_stack(screen):
continue # Screen not found, try next one
with self.batch_update():
while not self.__screen_eq(self.screen_stack[-1], screen):
with self.prevent(ScreenResume):
await self.pop_screen()
self.screen.post_message(ScreenResume())
break # Screen found and located on top of the stack, stop
else:
raise ScreenNotFoundError(
f"None of the {screens} screens was found in stack.\nScreen stack: {self.screen_stack}"
)
return AwaitComplete(_pop_screen_until()).call_next(self)
def _is_screen_in_stack(self, screen_to_check: str | type[Screen[ScreenResultType]]) -> bool:
return any(self.__screen_eq(screen, screen_to_check) for screen in self.screen_stack)
def _screen_eq(self, screen: Screen[ScreenResultType], other: str | type[Screen[ScreenResultType]]) -> bool:
if isinstance(other, str):
return screen.__class__.__name__ == other
return isinstance(screen, other)
Also related: #3126 (regression) and #3127
I think it would be really great if:
- batch_update could respect things like changing screen_stack, changing focus etc. Not only visual changes to the current screen. Consider also such a situation:
with self.app.batch_update(): # ensure no flicker when taking multiple actions on screens
await self.app.pop_screen() # drop current "item" screen
await self.app.push_screen(Cart()) # we want to go back to this Cart while being on the TransactionSummaryFromCart and "escape" binding is pressed
await self.app.push_screen(TransactionSummaryFromCart())
- There was some official method allowing for popping multiple screens in a correct way - either something like
pop_screen_until(DesiredScreen)
orpop_screen(amount=2)