mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' into fix-1372
This commit is contained in:
@@ -12,12 +12,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Added the coroutines `Animator.wait_for_fully_idle` and `pilot.wait_for_scheduled_animations` that allow waiting for all current and scheduled animations https://github.com/Textualize/textual/issues/1658
|
||||
- Added the method `Animator.is_being_animated` that checks if an attribute of an object is being animated or is scheduled for animation
|
||||
|
||||
### Changed
|
||||
|
||||
- Breaking change: `TreeNode` can no longer be imported from `textual.widgets`; it is now available via `from textual.widgets.tree import TreeNode`. https://github.com/Textualize/textual/pull/1637
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed stuck screen https://github.com/Textualize/textual/issues/1632
|
||||
- Fixed relative units in `grid-rows` and `grid-columns` being computed with respect to the wrong dimension https://github.com/Textualize/textual/issues/1406
|
||||
- Fixed bug with animations that were triggered back to back, where the second one wouldn't start https://github.com/Textualize/textual/issues/1372
|
||||
- Fixed bug with animations that were scheduled where all but the first would be skipped https://github.com/Textualize/textual/issues/1372
|
||||
- Programmatically setting `overflow_x`/`overflow_y` refreshes the layout correctly https://github.com/Textualize/textual/issues/1616
|
||||
- Fixed double-paste into `Input` https://github.com/Textualize/textual/issues/1657
|
||||
|
||||
## [0.10.1] - 2023-01-20
|
||||
|
||||
@@ -4,7 +4,8 @@ from pathlib import Path
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Header, Footer, Tree, TreeNode
|
||||
from textual.widgets import Header, Footer, Tree
|
||||
from textual.widgets.tree import TreeNode
|
||||
|
||||
|
||||
class TreeApp(App):
|
||||
|
||||
@@ -704,6 +704,9 @@ class StringEnumProperty:
|
||||
"""
|
||||
return obj.get_rule(self.name, self._default)
|
||||
|
||||
def _before_refresh(self, obj: StylesBase, value: str | None) -> None:
|
||||
"""Do any housekeeping before asking for a layout refresh after a value change."""
|
||||
|
||||
def __set__(self, obj: StylesBase, value: str | None = None):
|
||||
"""Set the string property and ensure it is in the set of allowed values.
|
||||
|
||||
@@ -717,6 +720,7 @@ class StringEnumProperty:
|
||||
_rich_traceback_omit = True
|
||||
if value is None:
|
||||
if obj.clear_rule(self.name):
|
||||
self._before_refresh(obj, value)
|
||||
obj.refresh(layout=self._layout)
|
||||
else:
|
||||
if value not in self._valid_values:
|
||||
@@ -729,9 +733,20 @@ class StringEnumProperty:
|
||||
),
|
||||
)
|
||||
if obj.set_rule(self.name, value):
|
||||
self._before_refresh(obj, value)
|
||||
obj.refresh(layout=self._layout)
|
||||
|
||||
|
||||
class OverflowProperty(StringEnumProperty):
|
||||
"""Descriptor for overflow styles that forces widgets to refresh scrollbars."""
|
||||
|
||||
def _before_refresh(self, obj: StylesBase, value: str | None) -> None:
|
||||
from ..widget import Widget # Avoid circular import
|
||||
|
||||
if isinstance(obj.node, Widget):
|
||||
obj.node._refresh_scrollbars()
|
||||
|
||||
|
||||
class NameProperty:
|
||||
"""Descriptor for getting and setting name properties."""
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ from ._style_properties import (
|
||||
NameListProperty,
|
||||
NameProperty,
|
||||
OffsetProperty,
|
||||
OverflowProperty,
|
||||
ScalarListProperty,
|
||||
ScalarProperty,
|
||||
SpacingProperty,
|
||||
@@ -246,8 +247,8 @@ class StylesBase(ABC):
|
||||
|
||||
dock = DockProperty()
|
||||
|
||||
overflow_x = StringEnumProperty(VALID_OVERFLOW, "hidden")
|
||||
overflow_y = StringEnumProperty(VALID_OVERFLOW, "hidden")
|
||||
overflow_x = OverflowProperty(VALID_OVERFLOW, "hidden")
|
||||
overflow_y = OverflowProperty(VALID_OVERFLOW, "hidden")
|
||||
|
||||
layer = NameProperty()
|
||||
layers = NameListProperty()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import Lock, wait
|
||||
from asyncio import Lock, create_task, wait
|
||||
from collections import Counter
|
||||
from fractions import Fraction
|
||||
from itertools import islice
|
||||
@@ -922,7 +921,7 @@ class Widget(DOMNode):
|
||||
show_horizontal = self.show_horizontal_scrollbar
|
||||
if overflow_x == "hidden":
|
||||
show_horizontal = False
|
||||
if overflow_x == "scroll":
|
||||
elif overflow_x == "scroll":
|
||||
show_horizontal = True
|
||||
elif overflow_x == "auto":
|
||||
show_horizontal = self.virtual_size.width > width
|
||||
@@ -1974,13 +1973,14 @@ class Widget(DOMNode):
|
||||
"""
|
||||
show_vertical_scrollbar, show_horizontal_scrollbar = self.scrollbars_enabled
|
||||
|
||||
scrollbar_size_horizontal = self.styles.scrollbar_size_horizontal
|
||||
scrollbar_size_vertical = self.styles.scrollbar_size_vertical
|
||||
styles = self.styles
|
||||
scrollbar_size_horizontal = styles.scrollbar_size_horizontal
|
||||
scrollbar_size_vertical = styles.scrollbar_size_vertical
|
||||
|
||||
if self.styles.scrollbar_gutter == "stable":
|
||||
if styles.scrollbar_gutter == "stable":
|
||||
# Let's _always_ reserve some space, whether the scrollbar is actually displayed or not:
|
||||
show_vertical_scrollbar = True
|
||||
scrollbar_size_vertical = self.styles.scrollbar_size_vertical
|
||||
scrollbar_size_vertical = styles.scrollbar_size_vertical
|
||||
|
||||
if show_horizontal_scrollbar and show_vertical_scrollbar:
|
||||
(region, _, _, _) = region.split(
|
||||
|
||||
@@ -23,7 +23,6 @@ if typing.TYPE_CHECKING:
|
||||
from ._static import Static
|
||||
from ._text_log import TextLog
|
||||
from ._tree import Tree
|
||||
from ._tree_node import TreeNode
|
||||
from ._welcome import Welcome
|
||||
from ..widget import Widget
|
||||
|
||||
@@ -44,7 +43,6 @@ __all__ = [
|
||||
"Static",
|
||||
"TextLog",
|
||||
"Tree",
|
||||
"TreeNode",
|
||||
"Welcome",
|
||||
]
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from ._tree import TreeNode as TreeNode
|
||||
5
src/textual/widgets/data_table.py
Normal file
5
src/textual/widgets/data_table.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Make non-widget DataTable support classes available."""
|
||||
|
||||
from ._data_table import Column, Row
|
||||
|
||||
__all__ = ["Column", "Row"]
|
||||
5
src/textual/widgets/tree.py
Normal file
5
src/textual/widgets/tree.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Make non-widget Tree support classes available."""
|
||||
|
||||
from ._tree import TreeNode
|
||||
|
||||
__all__ = ["TreeNode"]
|
||||
37
tests/test_overflow_change.py
Normal file
37
tests/test_overflow_change.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""Regression test for #1616 https://github.com/Textualize/textual/issues/1616"""
|
||||
import pytest
|
||||
|
||||
|
||||
from textual.app import App
|
||||
from textual.containers import Vertical
|
||||
|
||||
|
||||
async def test_overflow_change_updates_virtual_size_appropriately():
|
||||
class MyApp(App):
|
||||
def compose(self):
|
||||
yield Vertical()
|
||||
|
||||
app = MyApp()
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
vertical = app.query_one(Vertical)
|
||||
|
||||
height = vertical.virtual_size.height
|
||||
|
||||
vertical.styles.overflow_x = "scroll"
|
||||
await pilot.pause() # Let changes propagate.
|
||||
assert vertical.virtual_size.height < height
|
||||
|
||||
vertical.styles.overflow_x = "hidden"
|
||||
await pilot.pause()
|
||||
assert vertical.virtual_size.height == height
|
||||
|
||||
width = vertical.virtual_size.width
|
||||
|
||||
vertical.styles.overflow_y = "scroll"
|
||||
await pilot.pause()
|
||||
assert vertical.virtual_size.width < width
|
||||
|
||||
vertical.styles.overflow_y = "hidden"
|
||||
await pilot.pause()
|
||||
assert vertical.virtual_size.width == width
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
from textual.widgets import Tree, TreeNode
|
||||
from textual.widgets import Tree
|
||||
from textual.widgets.tree import TreeNode
|
||||
|
||||
|
||||
def label_of(node: TreeNode[None]):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from textual.widgets import Tree, TreeNode
|
||||
from textual.widgets import Tree
|
||||
from textual.widgets.tree import TreeNode
|
||||
from rich.text import Text
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from textual.widgets import TreeNode, Tree
|
||||
from textual.widgets import Tree
|
||||
|
||||
|
||||
def test_tree_node_parent() -> None:
|
||||
|
||||
Reference in New Issue
Block a user