mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fix for auto width and relative dimensions
This commit is contained in:
@@ -46,7 +46,8 @@ class Layout(ABC):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> int:
|
def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> int:
|
||||||
"""Get the width of the content.
|
"""Get the width of the content. In Horizontal layout, the content width of
|
||||||
|
a widget is the sum of the widths of its children.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
widget (Widget): The container widget.
|
widget (Widget): The container widget.
|
||||||
@@ -56,19 +57,17 @@ class Layout(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
int: Width of the content.
|
int: Width of the content.
|
||||||
"""
|
"""
|
||||||
width: int | None = None
|
if not widget.children:
|
||||||
gutter_width = widget.gutter.width
|
|
||||||
for child in widget.displayed_children:
|
|
||||||
if not child.is_container:
|
|
||||||
child_width = (
|
|
||||||
child.get_content_width(container, viewport)
|
|
||||||
+ gutter_width
|
|
||||||
+ child.gutter.width
|
|
||||||
)
|
|
||||||
width = child_width if width is None else max(width, child_width)
|
|
||||||
if width is None:
|
|
||||||
width = 0
|
width = 0
|
||||||
|
else:
|
||||||
|
placements, _, _ = widget._arrange(Size(0, 0))
|
||||||
|
width = max(
|
||||||
|
[
|
||||||
|
placement.region.right + placement.margin.right
|
||||||
|
for placement in placements
|
||||||
|
],
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
return width
|
return width
|
||||||
|
|
||||||
def get_content_height(
|
def get_content_height(
|
||||||
@@ -85,12 +84,16 @@ class Layout(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
int: Content height (in lines).
|
int: Content height (in lines).
|
||||||
"""
|
"""
|
||||||
if not widget.displayed_children:
|
if not widget.children:
|
||||||
height = 0
|
height = 0
|
||||||
else:
|
else:
|
||||||
placements, *_ = widget._arrange(Size(width, container.height))
|
placements, _, _ = widget._arrange(Size(width, 0))
|
||||||
height = max(
|
height = max(
|
||||||
placement.region.bottom + placement.margin.bottom
|
[
|
||||||
for placement in placements
|
placement.region.bottom + placement.margin.bottom
|
||||||
|
for placement in placements
|
||||||
|
],
|
||||||
|
default=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
return height
|
return height
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
Label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
ColorButtons {
|
ColorButtons {
|
||||||
dock: left;
|
dock: left;
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ LocationLink {
|
|||||||
padding: 1 2;
|
padding: 1 2;
|
||||||
background: $boost;
|
background: $boost;
|
||||||
color: $text;
|
color: $text;
|
||||||
|
box-sizing: content-box;
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,25 +59,3 @@ class HorizontalLayout(Layout):
|
|||||||
x = next_x + margin
|
x = next_x + margin
|
||||||
|
|
||||||
return placements, set(displayed_children)
|
return placements, set(displayed_children)
|
||||||
|
|
||||||
def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> int:
|
|
||||||
"""Get the width of the content. In Horizontal layout, the content width of
|
|
||||||
a widget is the sum of the widths of its children.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
widget (Widget): The container widget.
|
|
||||||
container (Size): The container size.
|
|
||||||
viewport (Size): The viewport size.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: Width of the content.
|
|
||||||
"""
|
|
||||||
if not widget.displayed_children:
|
|
||||||
width = 0
|
|
||||||
else:
|
|
||||||
placements, *_ = widget._arrange(container)
|
|
||||||
width = max(
|
|
||||||
placement.region.right + placement.margin.right
|
|
||||||
for placement in placements
|
|
||||||
)
|
|
||||||
return width
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from rich.text import Text
|
|||||||
from . import errors, events, messages
|
from . import errors, events, messages
|
||||||
from ._animator import DEFAULT_EASING, Animatable, BoundAnimator, EasingFunction
|
from ._animator import DEFAULT_EASING, Animatable, BoundAnimator, EasingFunction
|
||||||
from ._arrange import DockArrangeResult, arrange
|
from ._arrange import DockArrangeResult, arrange
|
||||||
|
from ._cache import LRUCache
|
||||||
from ._context import active_app
|
from ._context import active_app
|
||||||
from ._easing import DEFAULT_SCROLL_EASING
|
from ._easing import DEFAULT_SCROLL_EASING
|
||||||
from ._layout import Layout
|
from ._layout import Layout
|
||||||
@@ -246,8 +247,8 @@ class Widget(DOMNode):
|
|||||||
self._content_width_cache: tuple[object, int] = (None, 0)
|
self._content_width_cache: tuple[object, int] = (None, 0)
|
||||||
self._content_height_cache: tuple[object, int] = (None, 0)
|
self._content_height_cache: tuple[object, int] = (None, 0)
|
||||||
|
|
||||||
self._arrangement: DockArrangeResult | None = None
|
self._arrangement_cache_updates: int = -1
|
||||||
self._arrangement_cache_key: tuple[int, Size] = (-1, Size())
|
self._arrangement_cache: LRUCache[Size, DockArrangeResult] = LRUCache(4)
|
||||||
|
|
||||||
self._styles_cache = StylesCache()
|
self._styles_cache = StylesCache()
|
||||||
self._rich_style_cache: dict[str, tuple[Style, Style]] = {}
|
self._rich_style_cache: dict[str, tuple[Style, Style]] = {}
|
||||||
@@ -457,21 +458,25 @@ class Widget(DOMNode):
|
|||||||
Returns:
|
Returns:
|
||||||
ArrangeResult: Widget locations.
|
ArrangeResult: Widget locations.
|
||||||
"""
|
"""
|
||||||
|
assert self.is_container
|
||||||
|
|
||||||
arrange_cache_key = (self.children._updates, size)
|
if self._arrangement_cache_updates != self.children._updates:
|
||||||
if (
|
self._arrangement_cache_updates = self.children._updates
|
||||||
self._arrangement is not None
|
self._arrangement_cache.clear()
|
||||||
and arrange_cache_key == self._arrangement_cache_key
|
|
||||||
):
|
|
||||||
return self._arrangement
|
|
||||||
|
|
||||||
self._arrangement_cache_key = arrange_cache_key
|
cached_arrangement = self._arrangement_cache.get(size, None)
|
||||||
self._arrangement = arrange(self, self.children, size, self.screen.size)
|
if cached_arrangement is not None:
|
||||||
return self._arrangement
|
return cached_arrangement
|
||||||
|
|
||||||
|
arrangement = self._arrangement_cache[size] = arrange(
|
||||||
|
self, self.children, size, self.screen.size
|
||||||
|
)
|
||||||
|
return arrangement
|
||||||
|
|
||||||
def _clear_arrangement_cache(self) -> None:
|
def _clear_arrangement_cache(self) -> None:
|
||||||
"""Clear arrangement cache, forcing a new arrange operation."""
|
"""Clear arrangement cache, forcing a new arrange operation."""
|
||||||
self._arrangement = None
|
# self._arrangement = None
|
||||||
|
self._arrangement_cache.clear()
|
||||||
|
|
||||||
def _get_virtual_dom(self) -> Iterable[Widget]:
|
def _get_virtual_dom(self) -> Iterable[Widget]:
|
||||||
"""Get widgets not part of the DOM.
|
"""Get widgets not part of the DOM.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
37
tests/snapshot_tests/snapshot_apps/columns_height.py
Normal file
37
tests/snapshot_tests/snapshot_apps/columns_height.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.containers import Horizontal
|
||||||
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
|
class HeightApp(App[None]):
|
||||||
|
|
||||||
|
CSS = """
|
||||||
|
Horizontal {
|
||||||
|
border: solid red;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
Static {
|
||||||
|
border: solid green;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fill_parent {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#static {
|
||||||
|
height: 16;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Horizontal(
|
||||||
|
Static("As tall as container", id="fill_parent"),
|
||||||
|
Static("This has default\nheight\nbut a\nfew lines"),
|
||||||
|
Static("I have a static height", id="static"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
HeightApp().run()
|
||||||
@@ -16,7 +16,7 @@ class Body1(Vertical):
|
|||||||
|
|
||||||
class Body2(Vertical):
|
class Body2(Vertical):
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Label("I'm sorry, Dave. I'm afraid I can't do that. " * 300)
|
yield Label("My God! It's full of stars! " * 300)
|
||||||
|
|
||||||
|
|
||||||
class Good(Screen):
|
class Good(Screen):
|
||||||
@@ -52,6 +52,10 @@ class Layers(App[None]):
|
|||||||
background: red;
|
background: red;
|
||||||
color: yellow;
|
color: yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Body2 {
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SCREENS = {"good": Good, "bad": Bad}
|
SCREENS = {"good": Good, "bad": Bad}
|
||||||
|
|||||||
@@ -139,15 +139,18 @@ def test_multiple_css(snap_compare):
|
|||||||
|
|
||||||
|
|
||||||
def test_order_independence(snap_compare):
|
def test_order_independence(snap_compare):
|
||||||
# Interaction between multiple CSS files and app-level/classvar CSS
|
|
||||||
assert snap_compare("snapshot_apps/order_independence.py")
|
assert snap_compare("snapshot_apps/order_independence.py")
|
||||||
|
|
||||||
|
|
||||||
def test_order_independence_toggle(snap_compare):
|
def test_order_independence_toggle(snap_compare):
|
||||||
# Interaction between multiple CSS files and app-level/classvar CSS
|
|
||||||
assert snap_compare("snapshot_apps/order_independence.py", press="t,_")
|
assert snap_compare("snapshot_apps/order_independence.py", press="t,_")
|
||||||
|
|
||||||
|
|
||||||
|
def test_columns_height(snap_compare):
|
||||||
|
# Interaction with height auto, and relative heights to make columns
|
||||||
|
assert snap_compare("snapshot_apps/columns_height.py")
|
||||||
|
|
||||||
|
|
||||||
# --- Other ---
|
# --- Other ---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user