mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Expanding fr (#2221)
* forced fr to expand * margin size * remove comment * missing snapshot * snapshot tests * changelog * optimize * snapshot fix * snapshot update * snapshot and fixes * docstrings [skip ci]
This commit is contained in:
File diff suppressed because one or more lines are too long
40
tests/snapshot_tests/snapshot_apps/auto_fr.py
Normal file
40
tests/snapshot_tests/snapshot_apps/auto_fr.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Label
|
||||
|
||||
|
||||
class FRApp(App):
|
||||
CSS = """
|
||||
Screen {
|
||||
align: center middle;
|
||||
border: solid cyan;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 30;
|
||||
height: auto;
|
||||
border: solid green;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#child {
|
||||
height: 1fr;
|
||||
border: solid red;
|
||||
}
|
||||
|
||||
#bottom {
|
||||
margin: 1 2;
|
||||
background: $primary;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Widget(id="container"):
|
||||
yield Label("Hello one line", id="top")
|
||||
yield Widget(id="child")
|
||||
yield Label("Two\nLines with 1x2 margin", id="bottom")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = FRApp()
|
||||
app.run()
|
||||
35
tests/snapshot_tests/snapshot_apps/fr_margins.py
Normal file
35
tests/snapshot_tests/snapshot_apps/fr_margins.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Label
|
||||
from textual.containers import Container
|
||||
|
||||
|
||||
# Test fr dimensions and margins work in an auto container
|
||||
# https://github.com/Textualize/textual/issues/2220
|
||||
class TestApp(App):
|
||||
CSS = """
|
||||
Container {
|
||||
background: green 20%;
|
||||
border: heavy green;
|
||||
width: auto;
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
Label {
|
||||
background: green 20%;
|
||||
width: 1fr;
|
||||
height: 1fr;
|
||||
margin: 2 2;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Container():
|
||||
yield Label("Hello")
|
||||
yield Label("World")
|
||||
yield Label("!!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TestApp()
|
||||
app.run()
|
||||
@@ -388,6 +388,16 @@ def test_dock_scroll(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "dock_scroll.py", terminal_size=(80, 25))
|
||||
|
||||
|
||||
def test_auto_fr(snap_compare):
|
||||
# https://github.com/Textualize/textual/issues/2220
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "auto_fr.py", terminal_size=(80, 25))
|
||||
|
||||
|
||||
def test_fr_margins(snap_compare):
|
||||
# https://github.com/Textualize/textual/issues/2220
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "fr_margins.py", terminal_size=(80, 25))
|
||||
|
||||
|
||||
def test_scroll_visible(snap_compare):
|
||||
# https://github.com/Textualize/textual/issues/2181
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_visible.py", press=["t"])
|
||||
|
||||
@@ -2,206 +2,190 @@ from __future__ import annotations
|
||||
|
||||
from fractions import Fraction
|
||||
|
||||
from textual.box_model import BoxModel, get_box_model
|
||||
from textual.box_model import BoxModel
|
||||
from textual.css.styles import Styles
|
||||
from textual.geometry import Size, Spacing
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
def test_content_box():
|
||||
styles = Styles()
|
||||
styles.width = 10
|
||||
styles.height = 8
|
||||
styles.padding = 1
|
||||
styles.border = ("solid", "red")
|
||||
|
||||
one = Fraction(1)
|
||||
|
||||
class TestWidget(Widget):
|
||||
def get_content_width(self, container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
def get_content_height(self, container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
widget = TestWidget()
|
||||
|
||||
# border-box is default
|
||||
assert styles.box_sizing == "border-box"
|
||||
assert widget.styles.box_sizing == "border-box"
|
||||
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
widget.styles.width = 10
|
||||
widget.styles.height = 8
|
||||
widget.styles.padding = 1
|
||||
widget.styles.border = ("solid", "red")
|
||||
|
||||
def get_auto_height(container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
box_model = widget._get_box_model(
|
||||
Size(60, 20),
|
||||
Size(80, 24),
|
||||
one,
|
||||
one,
|
||||
)
|
||||
# Size should be inclusive of padding / border
|
||||
assert box_model == BoxModel(Fraction(10), Fraction(8), Spacing(0, 0, 0, 0))
|
||||
|
||||
# Switch to content-box
|
||||
styles.box_sizing = "content-box"
|
||||
widget.styles.box_sizing = "content-box"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
# width and height have added padding / border to accommodate content
|
||||
assert box_model == BoxModel(Fraction(14), Fraction(12), Spacing(0, 0, 0, 0))
|
||||
|
||||
|
||||
def test_width():
|
||||
"""Test width settings."""
|
||||
styles = Styles()
|
||||
|
||||
one = Fraction(1)
|
||||
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
return 10
|
||||
class TestWidget(Widget):
|
||||
def get_content_width(self, container: Size, parent: Size) -> int:
|
||||
return 10
|
||||
|
||||
def get_auto_height(container: Size, parent: Size, width: int) -> int:
|
||||
return 10
|
||||
def get_content_height(self, container: Size, parent: Size, width: int) -> int:
|
||||
return 10
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
widget = TestWidget()
|
||||
styles = widget.styles
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(60), Fraction(20), Spacing(0, 0, 0, 0))
|
||||
|
||||
# Add a margin and check that it is reported
|
||||
styles.margin = (1, 2, 3, 4)
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
# Set width to auto-detect
|
||||
styles.width = "auto"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
# Setting width to auto should call get_auto_width
|
||||
assert box_model == BoxModel(Fraction(10), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
# Set width to 100 vw which should make it the width of the parent
|
||||
styles.width = "100vw"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(80), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
# Set the width to 100% should make it fill the container size
|
||||
styles.width = "100%"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
styles.width = "100vw"
|
||||
styles.max_width = "50%"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(30), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
|
||||
def test_height():
|
||||
"""Test height settings."""
|
||||
styles = Styles()
|
||||
|
||||
one = Fraction(1)
|
||||
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
return 10
|
||||
class TestWidget(Widget):
|
||||
def get_content_width(self, container: Size, parent: Size) -> int:
|
||||
return 10
|
||||
|
||||
def get_auto_height(container: Size, parent: Size, width: int) -> int:
|
||||
return 10
|
||||
def get_content_height(self, container: Size, parent: Size, width: int) -> int:
|
||||
return 10
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
widget = TestWidget()
|
||||
styles = widget.styles
|
||||
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(60), Fraction(20), Spacing(0, 0, 0, 0))
|
||||
|
||||
# Add a margin and check that it is reported
|
||||
styles.margin = (1, 2, 3, 4)
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
# Set height to 100 vw which should make it the height of the parent
|
||||
styles.height = "100vh"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(24), Spacing(1, 2, 3, 4))
|
||||
|
||||
# Set the height to 100% should make it fill the container size
|
||||
styles.height = "100%"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
styles.height = "auto"
|
||||
styles.margin = 2
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
print(box_model)
|
||||
assert box_model == BoxModel(Fraction(56), Fraction(10), Spacing(2, 2, 2, 2))
|
||||
|
||||
styles.margin = 1, 2, 3, 4
|
||||
styles.height = "100vh"
|
||||
styles.max_height = "50%"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(10), Spacing(1, 2, 3, 4))
|
||||
|
||||
# Set height to auto and set content height to 0 to check if box collapses.
|
||||
styles.height = "auto"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, one, get_auto_width, lambda *_: 0
|
||||
)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(0), Spacing(1, 2, 3, 4))
|
||||
|
||||
|
||||
def test_max():
|
||||
"""Check that max_width and max_height are respected."""
|
||||
styles = Styles()
|
||||
one = Fraction(1)
|
||||
|
||||
class TestWidget(Widget):
|
||||
def get_content_width(self, container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
def get_content_height(self, container: Size, parent: Size, width: int) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
widget = TestWidget()
|
||||
styles = widget.styles
|
||||
|
||||
styles.width = 100
|
||||
styles.height = 80
|
||||
styles.max_width = 40
|
||||
styles.max_height = 30
|
||||
one = Fraction(1)
|
||||
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
def get_auto_height(container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(40, 30), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(40, 30), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(40), Fraction(30), Spacing(0, 0, 0, 0))
|
||||
|
||||
|
||||
def test_min():
|
||||
"""Check that min_width and min_height are respected."""
|
||||
styles = Styles()
|
||||
|
||||
one = Fraction(1)
|
||||
|
||||
class TestWidget(Widget):
|
||||
def get_content_width(self, container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
def get_content_height(self, container: Size, parent: Size, width: int) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
widget = TestWidget()
|
||||
styles = widget.styles
|
||||
styles.width = 10
|
||||
styles.height = 5
|
||||
styles.min_width = 40
|
||||
styles.min_height = 30
|
||||
one = Fraction(1)
|
||||
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
def get_auto_height(container: Size, parent: Size) -> int:
|
||||
assert False, "must not be called"
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(40, 30), Size(80, 24), one, one, get_auto_width, get_auto_height
|
||||
)
|
||||
box_model = widget._get_box_model(Size(40, 30), Size(80, 24), one, one)
|
||||
assert box_model == BoxModel(Fraction(40), Fraction(30), Spacing(0, 0, 0, 0))
|
||||
|
||||
Reference in New Issue
Block a user