Allow scrollbar size of zero (scrollable containers without scrollbars) (#3488)

* Remove the guard code which protects against `scrollbar-size-*:0`, update error messages indicating its supported now.

* Fix scrollbar region calculation to support zero-thickness scrollbars

* Add test ensuring zero-width scrollbars dont blow up

* Updating CHANGELOG regarding zero-thickness scrollbars

* Add note to scrollbar_size.md about zero width scrollbar
This commit is contained in:
Darren Burns
2023-10-10 15:37:13 +01:00
committed by GitHub
parent 5e4067fd05
commit 1a0cd7792d
6 changed files with 33 additions and 16 deletions

View File

@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `Input.clear` method https://github.com/Textualize/textual/pull/3430
- Added `TextArea.SelectionChanged` and `TextArea.Changed` messages https://github.com/Textualize/textual/pull/3442
- Added `wait_for_dismiss` parameter to `App.push_screen` https://github.com/Textualize/textual/pull/3477
- Allow scrollbar-size to be set to 0 to achieve scrollable containers with no visible scrollbars https://github.com/Textualize/textual/pull/3488
### Changed

View File

@@ -44,6 +44,11 @@ In this example we modify the size of the widget's scrollbar to be _much_ larger
In the next example we show three containers with differently sized scrollbars.
!!! tip
If you want to hide the scrollbar but still allow the container to scroll
using the mousewheel or keyboard, you can set the scrollbar size to `0`.
=== "Output"
```{.textual path="docs/examples/styles/scrollbar_size2.py"}

View File

@@ -587,9 +587,7 @@ def scrollbar_size_property_help_text(context: StylingContext) -> HelpText:
),
],
).get_by_context(context),
Bullet(
"<horizontal> and <vertical> must be positive integers, greater than zero"
),
Bullet("<horizontal> and <vertical> must be non-negative integers."),
],
)

View File

@@ -876,11 +876,7 @@ class StylesBuilder:
scrollbar_size_error(name, token2)
horizontal = int(token1.value)
if horizontal == 0:
scrollbar_size_error(name, token1)
vertical = int(token2.value)
if vertical == 0:
scrollbar_size_error(name, token2)
self.styles._rules["scrollbar_size_horizontal"] = horizontal
self.styles._rules["scrollbar_size_vertical"] = vertical
self._distribute_importance("scrollbar_size", ("horizontal", "vertical"))
@@ -895,8 +891,6 @@ class StylesBuilder:
if token.name != "number" or not token.value.isdigit():
self.error(name, token, scrollbar_size_single_axis_help_text(name))
value = int(token.value)
if value == 0:
self.error(name, token, scrollbar_size_single_axis_help_text(name))
self.styles._rules["scrollbar_size_vertical"] = value
def process_scrollbar_size_horizontal(self, name: str, tokens: list[Token]) -> None:
@@ -909,8 +903,6 @@ class StylesBuilder:
if token.name != "number" or not token.value.isdigit():
self.error(name, token, scrollbar_size_single_axis_help_text(name))
value = int(token.value)
if value == 0:
self.error(name, token, scrollbar_size_single_axis_help_text(name))
self.styles._rules["scrollbar_size_horizontal"] = value
def _process_grid_rows_or_columns(self, name: str, tokens: list[Token]) -> None:

View File

@@ -1220,7 +1220,7 @@ class Widget(DOMNode):
@property
def horizontal_scrollbar(self) -> ScrollBar:
"""The a horizontal scrollbar.
"""The horizontal scrollbar.
Note:
This will *create* a scrollbar if one doesn't exist.
@@ -2701,8 +2701,8 @@ class Widget(DOMNode):
horizontal_scrollbar_region,
scrollbar_corner_gap,
) = region.split(
-scrollbar_size_vertical,
-scrollbar_size_horizontal,
region.width - scrollbar_size_vertical,
region.height - scrollbar_size_horizontal,
)
if scrollbar_corner_gap:
yield self.scrollbar_corner, scrollbar_corner_gap
@@ -2719,7 +2719,7 @@ class Widget(DOMNode):
elif show_vertical_scrollbar:
window_region, scrollbar_region = region.split_vertical(
-scrollbar_size_vertical
region.width - scrollbar_size_vertical
)
if scrollbar_region:
scrollbar = self.vertical_scrollbar
@@ -2728,7 +2728,7 @@ class Widget(DOMNode):
yield scrollbar, scrollbar_region
elif show_horizontal_scrollbar:
window_region, scrollbar_region = region.split_horizontal(
-scrollbar_size_horizontal
region.height - scrollbar_size_horizontal
)
if scrollbar_region:
scrollbar = self.horizontal_scrollbar

View File

@@ -98,3 +98,24 @@ async def test_middle_container():
middle = app.query_one(Middle)
assert middle.size.width == 4
assert middle.size.height == app.size.height
async def test_scrollbar_zero_thickness():
"""Ensuring that scrollbars can be set to zero thickness."""
class ScrollbarZero(App):
CSS = """* {
scrollbar-size: 0 0;
scrollbar-size-vertical: 0; /* just exercising the parser */
scrollbar-size-horizontal: 0; /* exercise the parser */
}
"""
def compose(self) -> ComposeResult:
with Vertical():
for _ in range(10):
yield Label("Hello, world!")
app = ScrollbarZero()
async with app.run_test(size=(8, 6)):
pass