Skip to content

Commit

Permalink
Merge pull request #3718 from Textualize/compositor-ignore-unmounted
Browse files Browse the repository at this point in the history
Compositor ignores non-mounted widgets.
  • Loading branch information
rodrigogiraoserrao authored Nov 21, 2023
2 parents 67dd75a + a000994 commit 896aa9f
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Off-by-one in CSS error reporting https://github.com/Textualize/textual/issues/3625
- Loading indicators and app notifications overlapped in the wrong order https://github.com/Textualize/textual/issues/3677
- Widgets being loaded are disabled and have their scrolling explicitly disabled too https://github.com/Textualize/textual/issues/3677
- Method render on a widget could be called before mounting said widget https://github.com/Textualize/textual/issues/2914

### Added

Expand Down
3 changes: 3 additions & 0 deletions src/textual/_compositor.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,9 @@ def add_widget(
visible: Whether the widget should be visible by default.
This may be overridden by the CSS rule `visibility`.
"""
if not widget._is_mounted:
return

styles = widget.styles
visibility = styles.get_rule("visibility")
if visibility is not None:
Expand Down
1 change: 1 addition & 0 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2202,6 +2202,7 @@ async def invoke_ready_callback() -> None:
self.check_idle()
finally:
self._mounted_event.set()
self._is_mounted = True

Reactive._initialize_object(self)

Expand Down
9 changes: 9 additions & 0 deletions src/textual/message_pump.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ def __init__(self, parent: MessagePump | None = None) -> None:
self._last_idle: float = time()
self._max_idle: float | None = None
self._mounted_event = asyncio.Event()
self._is_mounted = False
"""Having this explicit Boolean is an optimization.
The same information could be retrieved from `self._mounted_event.is_set()`, but
we need to access this frequently in the compositor and the attribute with the
explicit Boolean value is faster than the two lookups and the function call.
"""
self._next_callbacks: list[events.Callback] = []
self._thread_id: int = threading.get_ident()

Expand Down Expand Up @@ -508,6 +515,7 @@ async def _pre_process(self) -> bool:
finally:
# This is critical, mount may be waiting
self._mounted_event.set()
self._is_mounted = True
return True

def _post_mount(self):
Expand Down Expand Up @@ -547,6 +555,7 @@ async def _process_messages_loop(self) -> None:
raise
except Exception as error:
self._mounted_event.set()
self._is_mounted = True
self.app._handle_exception(error)
break
finally:
Expand Down
2 changes: 1 addition & 1 deletion src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ def __init__(
@property
def is_mounted(self) -> bool:
"""Check if this widget is mounted."""
return self._mounted_event.is_set()
return self._is_mounted

@property
def siblings(self) -> list[Widget]:
Expand Down
27 changes: 27 additions & 0 deletions tests/test_mount.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Regression test for https://github.com/Textualize/textual/issues/2914
Make sure that calls to render only happen after a widget being mounted.
"""

import asyncio

from textual.app import App
from textual.widget import Widget


class W(Widget):
def render(self):
return self.renderable

async def on_mount(self):
await asyncio.sleep(0.1)
self.renderable = "1234"


async def test_render_only_after_mount():
"""Regression test for https://github.com/Textualize/textual/issues/2914"""
app = App()
async with app.run_test() as pilot:
app.mount(W())
app.mount(W())
await pilot.pause()

0 comments on commit 896aa9f

Please sign in to comment.