Skip to content

Commit 1a0cd77

Browse files
authored
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
1 parent 5e4067f commit 1a0cd77

File tree

6 files changed

+33
-16
lines changed

6 files changed

+33
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2828
- Added `Input.clear` method https://github.com/Textualize/textual/pull/3430
2929
- Added `TextArea.SelectionChanged` and `TextArea.Changed` messages https://github.com/Textualize/textual/pull/3442
3030
- Added `wait_for_dismiss` parameter to `App.push_screen` https://github.com/Textualize/textual/pull/3477
31+
- Allow scrollbar-size to be set to 0 to achieve scrollable containers with no visible scrollbars https://github.com/Textualize/textual/pull/3488
3132

3233
### Changed
3334

docs/styles/scrollbar_size.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ In this example we modify the size of the widget's scrollbar to be _much_ larger
4444

4545
In the next example we show three containers with differently sized scrollbars.
4646

47+
!!! tip
48+
49+
If you want to hide the scrollbar but still allow the container to scroll
50+
using the mousewheel or keyboard, you can set the scrollbar size to `0`.
51+
4752
=== "Output"
4853

4954
```{.textual path="docs/examples/styles/scrollbar_size2.py"}

src/textual/css/_help_text.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,9 +587,7 @@ def scrollbar_size_property_help_text(context: StylingContext) -> HelpText:
587587
),
588588
],
589589
).get_by_context(context),
590-
Bullet(
591-
"<horizontal> and <vertical> must be positive integers, greater than zero"
592-
),
590+
Bullet("<horizontal> and <vertical> must be non-negative integers."),
593591
],
594592
)
595593

src/textual/css/_styles_builder.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -876,11 +876,7 @@ def scrollbar_size_error(name: str, token: Token) -> None:
876876
scrollbar_size_error(name, token2)
877877

878878
horizontal = int(token1.value)
879-
if horizontal == 0:
880-
scrollbar_size_error(name, token1)
881879
vertical = int(token2.value)
882-
if vertical == 0:
883-
scrollbar_size_error(name, token2)
884880
self.styles._rules["scrollbar_size_horizontal"] = horizontal
885881
self.styles._rules["scrollbar_size_vertical"] = vertical
886882
self._distribute_importance("scrollbar_size", ("horizontal", "vertical"))
@@ -895,8 +891,6 @@ def process_scrollbar_size_vertical(self, name: str, tokens: list[Token]) -> Non
895891
if token.name != "number" or not token.value.isdigit():
896892
self.error(name, token, scrollbar_size_single_axis_help_text(name))
897893
value = int(token.value)
898-
if value == 0:
899-
self.error(name, token, scrollbar_size_single_axis_help_text(name))
900894
self.styles._rules["scrollbar_size_vertical"] = value
901895

902896
def process_scrollbar_size_horizontal(self, name: str, tokens: list[Token]) -> None:
@@ -909,8 +903,6 @@ def process_scrollbar_size_horizontal(self, name: str, tokens: list[Token]) -> N
909903
if token.name != "number" or not token.value.isdigit():
910904
self.error(name, token, scrollbar_size_single_axis_help_text(name))
911905
value = int(token.value)
912-
if value == 0:
913-
self.error(name, token, scrollbar_size_single_axis_help_text(name))
914906
self.styles._rules["scrollbar_size_horizontal"] = value
915907

916908
def _process_grid_rows_or_columns(self, name: str, tokens: list[Token]) -> None:

src/textual/widget.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ def vertical_scrollbar(self) -> ScrollBar:
12201220

12211221
@property
12221222
def horizontal_scrollbar(self) -> ScrollBar:
1223-
"""The a horizontal scrollbar.
1223+
"""The horizontal scrollbar.
12241224
12251225
Note:
12261226
This will *create* a scrollbar if one doesn't exist.
@@ -2701,8 +2701,8 @@ def _arrange_scrollbars(self, region: Region) -> Iterable[tuple[Widget, Region]]
27012701
horizontal_scrollbar_region,
27022702
scrollbar_corner_gap,
27032703
) = region.split(
2704-
-scrollbar_size_vertical,
2705-
-scrollbar_size_horizontal,
2704+
region.width - scrollbar_size_vertical,
2705+
region.height - scrollbar_size_horizontal,
27062706
)
27072707
if scrollbar_corner_gap:
27082708
yield self.scrollbar_corner, scrollbar_corner_gap
@@ -2719,7 +2719,7 @@ def _arrange_scrollbars(self, region: Region) -> Iterable[tuple[Widget, Region]]
27192719

27202720
elif show_vertical_scrollbar:
27212721
window_region, scrollbar_region = region.split_vertical(
2722-
-scrollbar_size_vertical
2722+
region.width - scrollbar_size_vertical
27232723
)
27242724
if scrollbar_region:
27252725
scrollbar = self.vertical_scrollbar
@@ -2728,7 +2728,7 @@ def _arrange_scrollbars(self, region: Region) -> Iterable[tuple[Widget, Region]]
27282728
yield scrollbar, scrollbar_region
27292729
elif show_horizontal_scrollbar:
27302730
window_region, scrollbar_region = region.split_horizontal(
2731-
-scrollbar_size_horizontal
2731+
region.height - scrollbar_size_horizontal
27322732
)
27332733
if scrollbar_region:
27342734
scrollbar = self.horizontal_scrollbar

tests/test_containers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,24 @@ def compose(self) -> ComposeResult:
9898
middle = app.query_one(Middle)
9999
assert middle.size.width == 4
100100
assert middle.size.height == app.size.height
101+
102+
103+
async def test_scrollbar_zero_thickness():
104+
"""Ensuring that scrollbars can be set to zero thickness."""
105+
106+
class ScrollbarZero(App):
107+
CSS = """* {
108+
scrollbar-size: 0 0;
109+
scrollbar-size-vertical: 0; /* just exercising the parser */
110+
scrollbar-size-horizontal: 0; /* exercise the parser */
111+
}
112+
"""
113+
114+
def compose(self) -> ComposeResult:
115+
with Vertical():
116+
for _ in range(10):
117+
yield Label("Hello, world!")
118+
119+
app = ScrollbarZero()
120+
async with app.run_test(size=(8, 6)):
121+
pass

0 commit comments

Comments
 (0)