shutdown scrollbas

This commit is contained in:
Will McGugan
2022-10-29 13:29:32 +01:00
parent 2afb00f5b3
commit 12c553d195
7 changed files with 91 additions and 6 deletions

View File

@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.2] - Unreleased ## [0.2.2] - Unreleased
### Fixed
- Fixed issue where scrollbars weren't being unmounted
### Changed ### Changed
- DOMQuery now raises InvalidQueryFormat in response to invalid query strings, rather than cryptic CSS error - DOMQuery now raises InvalidQueryFormat in response to invalid query strings, rather than cryptic CSS error
@@ -19,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added App.run_async method - Added App.run_async method
- Added App.run_test context manager - Added App.run_test context manager
- Added auto_pilot to App.run and App.run_async - Added auto_pilot to App.run and App.run_async
- Added Widget._get_virtual_dom to get scrollbars
## [0.2.1] - 2022-10-23 ## [0.2.1] - 2022-10-23

View File

@@ -0,0 +1,18 @@
from textual.app import App, ComposeResult
from textual.containers import Container
class ScrollApp(App):
def compose(self) -> ComposeResult:
yield Container(
Container(), Container(),
id="top")
def key_r(self) -> None:
self.query_one("#top").remove()
if __name__ == "__main__":
app = ScrollApp()
app.run()

View File

@@ -1605,9 +1605,6 @@ class App(Generic[ReturnType], DOMNode):
await self._prune_node(widget) await self._prune_node(widget)
# for child in remove_widgets:
# await child._close_messages()
# self._unregister(child)
if parent is not None: if parent is not None:
parent.refresh(layout=True) parent.refresh(layout=True)
@@ -1625,7 +1622,7 @@ class App(Generic[ReturnType], DOMNode):
while stack: while stack:
widget = pop() widget = pop()
if widget.children: if widget.children:
yield list(widget.children) yield [*widget.children, *widget._get_virtual_dom()]
for child in widget.children: for child in widget.children:
push(child) push(child)

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
import click import click
from importlib_metadata import version from importlib_metadata import version
from textual.pilot import Pilot
from textual._import_app import import_app, AppFail from textual._import_app import import_app, AppFail
@@ -84,7 +85,12 @@ def run_app(import_name: str, dev: bool, press: str) -> None:
sys.exit(1) sys.exit(1)
press_keys = press.split(",") if press else None press_keys = press.split(",") if press else None
result = app.run(press=press_keys)
async def run_press_keys(pilot: Pilot) -> None:
if press_keys is not None:
await pilot.press(*press_keys)
result = app.run(auto_pilot=run_press_keys)
if result is not None: if result is not None:
from rich.console import Console from rich.console import Console

View File

@@ -359,6 +359,20 @@ class Widget(DOMNode):
"""Clear arrangement cache, forcing a new arrange operation.""" """Clear arrangement cache, forcing a new arrange operation."""
self._arrangement = None self._arrangement = None
def _get_virtual_dom(self) -> Iterable[Widget]:
"""Get widgets not part of the DOM.
Returns:
Iterable[Widget]: An iterable of Widgets.
"""
if self._horizontal_scrollbar is not None:
yield self._horizontal_scrollbar
if self._vertical_scrollbar is not None:
yield self._vertical_scrollbar
if self._scrollbar_corner is not None:
yield self._scrollbar_corner
def mount(self, *anon_widgets: Widget, **widgets: Widget) -> AwaitMount: def mount(self, *anon_widgets: Widget, **widgets: Widget) -> AwaitMount:
"""Mount child widgets (making this widget a container). """Mount child widgets (making this widget a container).
@@ -587,6 +601,7 @@ class Widget(DOMNode):
Returns: Returns:
ScrollBar: ScrollBar Widget. ScrollBar: ScrollBar Widget.
""" """
from .scrollbar import ScrollBar from .scrollbar import ScrollBar
if self._horizontal_scrollbar is not None: if self._horizontal_scrollbar is not None:
@@ -600,7 +615,7 @@ class Widget(DOMNode):
def _refresh_scrollbars(self) -> None: def _refresh_scrollbars(self) -> None:
"""Refresh scrollbar visibility.""" """Refresh scrollbar visibility."""
if not self.is_scrollable: if not self.is_scrollable or not self.container_size:
return return
styles = self.styles styles = self.styles

23
tests/test_auto_pilot.py Normal file
View File

@@ -0,0 +1,23 @@
from textual.app import App
from textual.pilot import Pilot
from textual import events
def test_auto_pilot() -> None:
keys_pressed: list[str] = []
class TestApp(App):
def on_key(self, event: events.Key) -> None:
keys_pressed.append(event.key)
async def auto_pilot(pilot: Pilot) -> None:
await pilot.press("tab", *"foo")
await pilot.pause(1 / 100)
await pilot.exit("bar")
app = TestApp()
result = app.run(headless=True, auto_pilot=auto_pilot)
assert result == "bar"
assert keys_pressed == ["tab", "f", "o", "o"]

21
tests/test_test_runner.py Normal file
View File

@@ -0,0 +1,21 @@
from textual.app import App
from textual import events
async def test_run_test() -> None:
"""Test the run_test context manager."""
keys_pressed: list[str] = []
class TestApp(App[str]):
def on_key(self, event: events.Key) -> None:
keys_pressed.append(event.key)
app = TestApp()
async with app.run_test() as pilot:
assert str(pilot) == "<Pilot app=TestApp(title='TestApp')>"
await pilot.press("tab", *"foo")
await pilot.pause(1 / 100)
await pilot.exit("bar")
assert app.return_value == "bar"
assert keys_pressed == ["tab", "f", "o", "o"]