|
18 | 18 | from typing import ( |
19 | 19 | TYPE_CHECKING, |
20 | 20 | Any, |
| 21 | + Callable, |
21 | 22 | Generic, |
22 | 23 | Iterable, |
23 | 24 | List, |
24 | 25 | Type, |
25 | 26 | TypeVar, |
26 | 27 | Union, |
27 | 28 | cast, |
28 | | - Callable, |
29 | 29 | ) |
30 | 30 | from weakref import WeakSet, WeakValueDictionary |
31 | 31 |
|
|
45 | 45 | from ._event_broker import NoHandler, extract_handler_actions |
46 | 46 | from ._filter import LineFilter, Monochrome |
47 | 47 | from ._path import _make_path_object_relative |
48 | | -from ._typing import TypeAlias, Final |
| 48 | +from ._typing import Final, TypeAlias |
| 49 | +from .await_remove import AwaitRemove |
49 | 50 | from .binding import Binding, Bindings |
50 | 51 | from .css.query import NoMatches |
51 | 52 | from .css.stylesheet import Stylesheet |
|
61 | 62 | from .reactive import Reactive |
62 | 63 | from .renderables.blank import Blank |
63 | 64 | from .screen import Screen |
64 | | -from .widget import AwaitMount, Widget |
| 65 | +from .widget import AwaitMount, MountError, Widget |
| 66 | + |
65 | 67 |
|
66 | 68 | if TYPE_CHECKING: |
67 | 69 | from .devtools.client import DevtoolsClient |
@@ -352,6 +354,7 @@ def __init__( |
352 | 354 | else None |
353 | 355 | ) |
354 | 356 | self._screenshot: str | None = None |
| 357 | + self._dom_lock = asyncio.Lock() |
355 | 358 |
|
356 | 359 | @property |
357 | 360 | def return_value(self) -> ReturnType | None: |
@@ -1951,6 +1954,48 @@ def _walk_children(self, root: Widget) -> Iterable[list[Widget]]: |
1951 | 1954 | for child in widget.children: |
1952 | 1955 | push(child) |
1953 | 1956 |
|
| 1957 | + def _remove_nodes(self, widgets: list[Widget]) -> AwaitRemove: |
| 1958 | + """Remove nodes from DOM, and return an awaitable that awaits cleanup. |
| 1959 | +
|
| 1960 | + Args: |
| 1961 | + widgets (list[Widget]): List of nodes to remvoe. |
| 1962 | +
|
| 1963 | + Returns: |
| 1964 | + AwaitRemove: Awaitable that returns when the nodes have been fully removed. |
| 1965 | + """ |
| 1966 | + |
| 1967 | + async def prune_widgets_task( |
| 1968 | + widgets: list[Widget], finished_event: asyncio.Event |
| 1969 | + ) -> None: |
| 1970 | + """Prune widgets as a background task. |
| 1971 | +
|
| 1972 | + Args: |
| 1973 | + widgets (list[Widget]): Widgets to prune. |
| 1974 | + finished_event (asyncio.Event): Event to set when complete. |
| 1975 | + """ |
| 1976 | + try: |
| 1977 | + await self._prune_nodes(widgets) |
| 1978 | + finally: |
| 1979 | + finished_event.set() |
| 1980 | + |
| 1981 | + removed_widgets = self._detach_from_dom(widgets) |
| 1982 | + self.refresh(layout=True) |
| 1983 | + |
| 1984 | + finished_event = asyncio.Event() |
| 1985 | + asyncio.create_task(prune_widgets_task(removed_widgets, finished_event)) |
| 1986 | + |
| 1987 | + return AwaitRemove(finished_event) |
| 1988 | + |
| 1989 | + async def _prune_nodes(self, widgets: list[Widget]) -> None: |
| 1990 | + """Remove nodes and children. |
| 1991 | +
|
| 1992 | + Args: |
| 1993 | + widgets (Widget): _description_ |
| 1994 | + """ |
| 1995 | + async with self._dom_lock: |
| 1996 | + for widget in widgets: |
| 1997 | + await self._prune_node(widget) |
| 1998 | + |
1954 | 1999 | async def _prune_node(self, root: Widget) -> None: |
1955 | 2000 | """Remove a node and its children. Children are removed before parents. |
1956 | 2001 |
|
|
0 commit comments