mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
shutdown scrollbas
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
18
sandbox/will/scroll_remove.py
Normal file
18
sandbox/will/scroll_remove.py
Normal 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()
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
23
tests/test_auto_pilot.py
Normal 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
21
tests/test_test_runner.py
Normal 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"]
|
||||||
Reference in New Issue
Block a user