Skip to content

Commit 343893d

Browse files
authored
Merge pull request #4037 from Textualize/fix-percentage-dimensions
fix for percentage dimensions
2 parents 9268f29 + 1fb3c64 commit 343893d

File tree

10 files changed

+1124
-931
lines changed

10 files changed

+1124
-931
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4343
- `SelectionList` option IDs are usable as soon as the widget is instantiated https://github.com/Textualize/textual/issues/3903
4444
- Fix issue with `Strip.crop` when crop window start aligned with strip end https://github.com/Textualize/textual/pull/3998
4545
- Fixed Strip.crop_extend https://github.com/Textualize/textual/pull/4011
46+
- Fix for percentage dimensions https://github.com/Textualize/textual/pull/4037
4647
- Fixed a crash if the `TextArea` language was set but tree-sitter language binaries were not installed https://github.com/Textualize/textual/issues/4045
4748
- Ensuring `TextArea.SelectionChanged` message only sends when the updated selection is different https://github.com/Textualize/textual/pull/3933
4849
- Fixed declaration after nested rule set causing a parse error https://github.com/Textualize/textual/pull/4012

src/textual/_arrange.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,4 @@ def _arrange_dock_widgets(
160160
_WidgetPlacement(dock_region, null_spacing, dock_widget, top_z, True)
161161
)
162162
dock_spacing = Spacing(top, right, bottom, left)
163-
164163
return (placements, dock_spacing)

src/textual/css/scalar.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ class Unit(Enum):
3737
AUTO = 8
3838

3939

40-
UNIT_EXCLUDES_BORDER = {Unit.CELLS, Unit.FRACTION, Unit.VIEW_WIDTH, Unit.VIEW_HEIGHT}
41-
4240
UNIT_SYMBOL = {
4341
Unit.CELLS: "",
4442
Unit.FRACTION: "fr",
@@ -206,10 +204,6 @@ def is_fraction(self) -> bool:
206204
"""Check if the unit is a fraction."""
207205
return self.unit == Unit.FRACTION
208206

209-
@property
210-
def excludes_border(self) -> bool:
211-
return self.unit in UNIT_EXCLUDES_BORDER
212-
213207
@property
214208
def cells(self) -> int | None:
215209
"""Check if the unit is explicit cells."""

src/textual/layouts/horizontal.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ def arrange(
2626
add_placement = placements.append
2727

2828
child_styles = [child.styles for child in children]
29-
box_margins: list[Spacing] = [styles.margin for styles in child_styles]
29+
box_margins: list[Spacing] = [
30+
styles.margin for styles in child_styles if styles.overlay != "screen"
31+
]
3032
if box_margins:
3133
resolve_margin = Size(
3234
sum(
@@ -36,7 +38,7 @@ def arrange(
3638
]
3739
)
3840
+ (box_margins[0].left + box_margins[-1].right),
39-
max(
41+
min(
4042
[
4143
margin_top + margin_bottom
4244
for margin_top, _, margin_bottom, _ in box_margins

src/textual/layouts/vertical.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def arrange(
2929
]
3030
if box_margins:
3131
resolve_margin = Size(
32-
max(
32+
min(
3333
[
3434
margin_right + margin_left
3535
for _, margin_right, _, margin_left in box_margins

src/textual/widget.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,18 +1071,14 @@ def _get_box_model(
10711071

10721072
# Container minus padding and border
10731073
content_container = container - gutter.totals
1074-
# The container including the content
1075-
sizing_container = content_container if is_border_box else container
10761074

10771075
if styles.width is None:
10781076
# No width specified, fill available space
10791077
content_width = Fraction(content_container.width - margin.width)
10801078
elif is_auto_width:
10811079
# When width is auto, we want enough space to always fit the content
10821080
content_width = Fraction(
1083-
self.get_content_width(
1084-
content_container - styles.margin.totals, viewport
1085-
)
1081+
self.get_content_width(content_container - margin.totals, viewport)
10861082
)
10871083
if styles.scrollbar_gutter == "stable" and styles.overflow_x == "auto":
10881084
content_width += styles.scrollbar_size_vertical
@@ -1095,15 +1091,15 @@ def _get_box_model(
10951091
# An explicit width
10961092
styles_width = styles.width
10971093
content_width = styles_width.resolve(
1098-
sizing_container - styles.margin.totals, viewport, width_fraction
1094+
container - margin.totals, viewport, width_fraction
10991095
)
1100-
if is_border_box and styles_width.excludes_border:
1096+
if is_border_box:
11011097
content_width -= gutter.width
11021098

11031099
if styles.min_width is not None:
11041100
# Restrict to minimum width, if set
11051101
min_width = styles.min_width.resolve(
1106-
content_container, viewport, width_fraction
1102+
container - margin.totals, viewport, width_fraction
11071103
)
11081104
if is_border_box:
11091105
min_width -= gutter.width
@@ -1112,7 +1108,7 @@ def _get_box_model(
11121108
if styles.max_width is not None:
11131109
# Restrict to maximum width, if set
11141110
max_width = styles.max_width.resolve(
1115-
content_container, viewport, width_fraction
1111+
container - margin.totals, viewport, width_fraction
11161112
)
11171113
if is_border_box:
11181114
max_width -= gutter.width
@@ -1139,15 +1135,15 @@ def _get_box_model(
11391135
styles_height = styles.height
11401136
# Explicit height set
11411137
content_height = styles_height.resolve(
1142-
sizing_container - styles.margin.totals, viewport, height_fraction
1138+
container - margin.totals, viewport, height_fraction
11431139
)
1144-
if is_border_box and styles_height.excludes_border:
1140+
if is_border_box:
11451141
content_height -= gutter.height
11461142

11471143
if styles.min_height is not None:
11481144
# Restrict to minimum height, if set
11491145
min_height = styles.min_height.resolve(
1150-
content_container, viewport, height_fraction
1146+
container - margin.totals, viewport, height_fraction
11511147
)
11521148
if is_border_box:
11531149
min_height -= gutter.height
@@ -1156,7 +1152,7 @@ def _get_box_model(
11561152
if styles.max_height is not None:
11571153
# Restrict maximum height, if set
11581154
max_height = styles.max_height.resolve(
1159-
content_container, viewport, height_fraction
1155+
container - margin.totals, viewport, height_fraction
11601156
)
11611157
if is_border_box:
11621158
max_height -= gutter.height

tests/snapshot_tests/__snapshots__/test_snapshots.ambr

Lines changed: 1066 additions & 906 deletions
Large diffs are not rendered by default.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from textual.app import App, ComposeResult
2+
from textual.widgets import Input, TextArea, Static, Button, Label
3+
4+
5+
class InputVsTextArea(App[None]):
6+
CSS = """
7+
Screen > *, Screen > *:focus {
8+
width: 50%;
9+
height: 1fr;
10+
border: solid red;
11+
}
12+
App #ruler {
13+
width: 1fr;
14+
height: 1;
15+
border: none;
16+
}
17+
"""
18+
19+
def compose(self) -> ComposeResult:
20+
yield Label("[reverse]0123456789[/]0123456789" * 4, id="ruler")
21+
22+
input = Input()
23+
input.cursor_blink = False
24+
yield input
25+
26+
text_area = TextArea()
27+
text_area.cursor_blink = False
28+
yield text_area
29+
30+
yield Static()
31+
yield Button()
32+
33+
34+
if __name__ == "__main__":
35+
InputVsTextArea().run()

tests/snapshot_tests/test_snapshots.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,3 +992,9 @@ def test_nested_specificity(snap_compare):
992992
def test_tab_rename(snap_compare):
993993
"""Test setting a new label for a tab amongst a TabbedContent."""
994994
assert snap_compare(SNAPSHOT_APPS_DIR / "tab_rename.py")
995+
996+
997+
def test_input_percentage_width(snap_compare):
998+
"""Check percentage widths work correctly."""
999+
# https://github.com/Textualize/textual/issues/3721
1000+
assert snap_compare(SNAPSHOT_APPS_DIR / "input_percentage_width.py")

tests/test_box_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def get_content_height(self, container: Size, parent: Size, width: int) -> int:
9090
styles.max_width = "50%"
9191

9292
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
93-
assert box_model == BoxModel(Fraction(30), Fraction(16), Spacing(1, 2, 3, 4))
93+
assert box_model == BoxModel(Fraction(27), Fraction(16), Spacing(1, 2, 3, 4))
9494

9595

9696
def test_height():
@@ -141,7 +141,7 @@ def get_content_height(self, container: Size, parent: Size, width: int) -> int:
141141
styles.max_height = "50%"
142142

143143
box_model = widget._get_box_model(Size(60, 20), Size(80, 24), one, one)
144-
assert box_model == BoxModel(Fraction(54), Fraction(10), Spacing(1, 2, 3, 4))
144+
assert box_model == BoxModel(Fraction(54), Fraction(8), Spacing(1, 2, 3, 4))
145145

146146

147147
def test_max():

0 commit comments

Comments
 (0)