Skip to content

Commit

Permalink
fix min width (#3712)
Browse files Browse the repository at this point in the history
* fix min width

* changelog

* snapshot tests

* whitespace
  • Loading branch information
willmcgugan authored Nov 21, 2023
1 parent ca68b86 commit a11ff16
Show file tree
Hide file tree
Showing 9 changed files with 601 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Breaking change: `Button` no longer inherits from `Static`, now it inherits directly from `Widget` https://github.com/Textualize/textual/issues/3603
- Rich markup in markdown headings is now escaped when building the TOC https://github.com/Textualize/textual/issues/3689
- Mechanics behind mouse clicks. See [this](https://github.com/Textualize/textual/pull/3495#issue-1934915047) for more details. https://github.com/Textualize/textual/pull/3495
- Breaking change: max/min-width/height now includes padding and border. https://github.com/Textualize/textual/pull/3712


## [0.41.0] - 2023-10-31
Expand Down
10 changes: 8 additions & 2 deletions src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,9 @@ def _get_box_model(
min_width = styles.min_width.resolve(
content_container, viewport, width_fraction
)
content_width = max(content_width, min_width)
if is_border_box:
min_width -= gutter.width
content_width = max(content_width, min_width, Fraction(0))

if styles.max_width is not None:
# Restrict to maximum width, if set
Expand Down Expand Up @@ -1066,13 +1068,17 @@ def _get_box_model(
min_height = styles.min_height.resolve(
content_container, viewport, height_fraction
)
content_height = max(content_height, min_height)
if is_border_box:
min_height -= gutter.height
content_height = max(content_height, min_height, Fraction(0))

if styles.max_height is not None:
# Restrict maximum height, if set
max_height = styles.max_height.resolve(
content_container, viewport, height_fraction
)
if is_border_box:
max_height -= gutter.height
content_height = min(content_height, max_height)

content_height = max(Fraction(0), content_height)
Expand Down
3 changes: 1 addition & 2 deletions src/textual/widgets/_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ class Input(Widget, can_focus=True):
padding: 0 2;
border: tall $background;
width: 100%;
height: 1;
min-height: 1;
height: 3;
}
Input:focus {
border: tall $accent;
Expand Down
2 changes: 1 addition & 1 deletion src/textual/widgets/_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class Select(Generic[SelectType], Vertical, can_focus=True):
width: 1fr;
display: none;
height: auto;
max-height: 10;
max-height: 12;
overlay: screen;
constrain: y;
}
Expand Down
474 changes: 474 additions & 0 deletions tests/snapshot_tests/__snapshots__/test_snapshots.ambr

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions tests/snapshot_tests/snapshot_apps/missing_vertical_scroll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from textual.app import App, ComposeResult
from textual.containers import Horizontal
from textual.widgets import OptionList


class MissingScrollbarApp(App[None]):
CSS = """
OptionList {
height: 1fr;
}
#left {
min-width: 25;
}
#middle {
width: 5fr;
}
#right {
min-width: 30;
}
"""

def compose(self) -> ComposeResult:
options = [str(n) for n in range(200)]
with Horizontal():
yield OptionList(*options, id="left")
yield OptionList(*options, id="middle")
yield OptionList(*options, id="right")


if __name__ == "__main__":
MissingScrollbarApp().run()
26 changes: 26 additions & 0 deletions tests/snapshot_tests/snapshot_apps/vertical_max_height.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.widgets import Placeholder


class VerticalApp(App):
CSS = """
#top {
height: 1fr;
border: white;
}
#bottom {
height:3fr;
border: white;
max-height: 10;
}
"""

def compose(self) -> ComposeResult:
with Vertical():
yield Placeholder(id="top")
yield Placeholder(id="bottom")


if __name__ == "__main__":
VerticalApp().run()
26 changes: 26 additions & 0 deletions tests/snapshot_tests/snapshot_apps/vertical_min_height.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.widgets import Placeholder


class VerticalApp(App):
CSS = """
#top {
min-height: 10;
height: 1fr;
border: white;
}
#bottom {
height:3fr;
border: white;
}
"""

def compose(self) -> ComposeResult:
with Vertical():
yield Placeholder(id="top")
yield Placeholder(id="bottom")


if __name__ == "__main__":
VerticalApp().run()
37 changes: 30 additions & 7 deletions tests/snapshot_tests/test_snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,9 @@ def test_scrollbar_thumb_height(snap_compare):
def test_css_hot_reloading(snap_compare, monkeypatch):
"""Regression test for https://github.com/Textualize/textual/issues/2063."""

monkeypatch.setenv("TEXTUAL", "debug") # This will make sure we create a file monitor.
monkeypatch.setenv(
"TEXTUAL", "debug"
) # This will make sure we create a file monitor.

async def run_before(pilot):
css_file = pilot.app.CSS_PATH
Expand All @@ -564,7 +566,9 @@ async def run_before(pilot):
def test_css_hot_reloading_on_screen(snap_compare, monkeypatch):
"""Regression test for https://github.com/Textualize/textual/issues/3454."""

monkeypatch.setenv("TEXTUAL", "debug") # This will make sure we create a file monitor.
monkeypatch.setenv(
"TEXTUAL", "debug"
) # This will make sure we create a file monitor.

async def run_before(pilot):
css_file = pilot.app.screen.CSS_PATH
Expand All @@ -573,14 +577,17 @@ async def run_before(pilot):
await pilot.app._on_css_change()

assert snap_compare(
SNAPSHOT_APPS_DIR / "hot_reloading_app_with_screen_css.py", run_before=run_before
SNAPSHOT_APPS_DIR / "hot_reloading_app_with_screen_css.py",
run_before=run_before,
)


def test_datatable_hot_reloading(snap_compare, monkeypatch):
"""Regression test for https://github.com/Textualize/textual/issues/3312."""

monkeypatch.setenv("TEXTUAL", "debug") # This will make sure we create a file monitor.
monkeypatch.setenv(
"TEXTUAL", "debug"
) # This will make sure we create a file monitor.

async def run_before(pilot):
css_file = pilot.app.CSS_PATH
Expand Down Expand Up @@ -703,9 +710,8 @@ async def run_before(pilot) -> None:


def test_command_palette(snap_compare) -> None:

async def run_before(pilot) -> None:
#await pilot.press("ctrl+backslash")
# await pilot.press("ctrl+backslash")
pilot.app.screen.query_one(Input).cursor_blink = False
await pilot.press("A")
await pilot.app.screen.workers.wait_for_complete()
Expand Down Expand Up @@ -907,4 +913,21 @@ def test_notifications_loading_overlap_order(snap_compare):
This tests that notifications stay on top of loading indicators and it also
tests that loading a widget will remove its scrollbars.
"""
assert snap_compare(SNAPSHOT_APPS_DIR / "notifications_above_loading.py", terminal_size=(80, 20))
assert snap_compare(
SNAPSHOT_APPS_DIR / "notifications_above_loading.py", terminal_size=(80, 20)
)


def test_missing_vertical_scroll(snap_compare):
"""Regression test for https://github.com/Textualize/textual/issues/3687"""
assert snap_compare(SNAPSHOT_APPS_DIR / "missing_vertical_scroll.py")


def test_vertical_min_height(snap_compare):
"""Test vertical min height takes border in to account."""
assert snap_compare(SNAPSHOT_APPS_DIR / "vertical_min_height.py")


def test_vertical_max_height(snap_compare):
"""Test vertical max height takes border in to account."""
assert snap_compare(SNAPSHOT_APPS_DIR / "vertical_max_height.py")

0 comments on commit a11ff16

Please sign in to comment.