Skip to content

Commit fec5571

Browse files
committed
scrollbar visibility rule
1 parent a77700c commit fec5571

File tree

7 files changed

+27
-8
lines changed

7 files changed

+27
-8
lines changed

src/textual/_compositor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ def add_widget(
694694
if (
695695
widget.show_vertical_scrollbar
696696
or widget.show_horizontal_scrollbar
697-
):
697+
) and styles.scrollbar_visibility == "visible":
698698
for chrome_widget, chrome_region in widget._arrange_scrollbars(
699699
container_region
700700
):

src/textual/css/_styles_builder.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
VALID_OVERLAY,
5353
VALID_POSITION,
5454
VALID_SCROLLBAR_GUTTER,
55+
VALID_SCROLLBAR_VISIBILITY,
5556
VALID_STYLE_FLAGS,
5657
VALID_TEXT_ALIGN,
5758
VALID_TEXT_OVERFLOW,
@@ -76,6 +77,7 @@
7677
Display,
7778
EdgeType,
7879
Overflow,
80+
ScrollbarVisibility,
7981
TextOverflow,
8082
TextWrap,
8183
Visibility,
@@ -768,6 +770,13 @@ def process_color(self, name: str, tokens: list[Token]) -> None:
768770
process_scrollbar_background_hover = process_color
769771
process_scrollbar_background_active = process_color
770772

773+
def process_scrollbar_visibility(self, name: str, tokens: list[Token]) -> None:
774+
"""Process scrollbar visibility rules."""
775+
self.styles._rules["scrollbar_visibility"] = cast(
776+
ScrollbarVisibility,
777+
self._process_enum(name, tokens, VALID_SCROLLBAR_VISIBILITY),
778+
)
779+
771780
process_link_color = process_color
772781
process_link_background = process_color
773782
process_link_color_hover = process_color

src/textual/css/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
VALID_TEXT_WRAP: Final = {"wrap", "nowrap"}
9191
VALID_TEXT_OVERFLOW: Final = {"clip", "fold", "ellipsis"}
9292
VALID_EXPAND: Final = {"greedy", "optimal"}
93+
VALID_SCROLLBAR_VISIBILITY: Final = {"visible", "hidden"}
9394

9495
HATCHES: Final = {
9596
"left": "╲",

src/textual/css/styles.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
VALID_OVERLAY,
4949
VALID_POSITION,
5050
VALID_SCROLLBAR_GUTTER,
51+
VALID_SCROLLBAR_VISIBILITY,
5152
VALID_TEXT_ALIGN,
5253
VALID_TEXT_OVERFLOW,
5354
VALID_TEXT_WRAP,
@@ -153,11 +154,10 @@ class RulesMap(TypedDict, total=False):
153154
scrollbar_background: Color
154155
scrollbar_background_hover: Color
155156
scrollbar_background_active: Color
156-
157157
scrollbar_gutter: ScrollbarGutter
158-
159158
scrollbar_size_vertical: int
160159
scrollbar_size_horizontal: int
160+
scrollbar_visibility: ScrollbarVisibility
161161

162162
align_horizontal: AlignHorizontal
163163
align_vertical: AlignVertical
@@ -242,6 +242,7 @@ class StylesBase:
242242
"scrollbar_background",
243243
"scrollbar_background_hover",
244244
"scrollbar_background_active",
245+
"scrollbar_visibility",
245246
"link_color",
246247
"link_background",
247248
"link_color_hover",
@@ -424,6 +425,10 @@ class StylesBase:
424425
"""Set the width of the vertical scrollbar (measured in cells)."""
425426
scrollbar_size_horizontal = IntegerProperty(default=1, layout=True)
426427
"""Set the height of the horizontal scrollbar (measured in cells)."""
428+
scrollbar_visibility = StringEnumProperty(
429+
VALID_SCROLLBAR_VISIBILITY, "visible", layout=True
430+
)
431+
"""Sets the visibility of the scrollbar."""
427432

428433
align_horizontal = StringEnumProperty(
429434
VALID_ALIGN_HORIZONTAL, "left", layout=True, refresh_children=True
@@ -1153,6 +1158,8 @@ def append_declaration(name: str, value: str) -> None:
11531158
append_declaration(
11541159
"scrollbar-size-vertical", str(self.scrollbar_size_vertical)
11551160
)
1161+
if "scrollbar_visibility" in rules:
1162+
append_declaration("scrollbar-visibility", self.scrollbar_visibility)
11561163

11571164
if "box_sizing" in rules:
11581165
append_declaration("box-sizing", self.box_sizing)

src/textual/css/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
TextWrap = Literal["wrap", "nowrap"]
4545
TextOverflow = Literal["clip", "fold", "ellipsis"]
4646
Expand = Literal["greedy", "expand"]
47+
ScrollbarVisibility = Literal["visible", "hidden"]
4748

4849
Specificity3 = Tuple[int, int, int]
4950
Specificity6 = Tuple[int, int, int, int, int, int]

src/textual/dom.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,8 +1723,6 @@ def _update_styles(self) -> None:
17231723
17241724
Should be called whenever CSS classes / pseudo classes change.
17251725
"""
1726-
if not self.is_attached or not self.screen.is_mounted:
1727-
return
17281726
try:
17291727
self.app.update_styles(self)
17301728
except NoActiveAppError:

src/textual/widget.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ class Widget(DOMNode):
346346
loading: Reactive[bool] = Reactive(False)
347347
"""If set to `True` this widget will temporarily be replaced with a loading indicator."""
348348

349-
virtual_size: Reactive[Size] = Reactive(Size(0, 0), layout=True)
349+
virtual_size: Reactive[Size] = Reactive(Size(0, 0), layout=True, repaint=False)
350350
"""The virtual (scrollable) [size][textual.geometry.Size] of the widget."""
351351

352352
has_focus: Reactive[bool] = Reactive(False, repaint=False)
@@ -3421,6 +3421,7 @@ def scroll_to_widget(
34213421
`True` if any scrolling has occurred in any descendant, otherwise `False`.
34223422
"""
34233423
# Grow the region by the margin so to keep the margin in view.
3424+
34243425
region = widget.virtual_region_with_margin
34253426
scrolled = False
34263427

@@ -3430,7 +3431,11 @@ def scroll_to_widget(
34303431
return False
34313432

34323433
while isinstance(widget.parent, Widget) and widget is not self:
3434+
if not region:
3435+
break
3436+
34333437
container = widget.parent
3438+
34343439
if widget.styles.dock != "none":
34353440
scroll_offset = Offset(0, 0)
34363441
else:
@@ -3454,13 +3459,11 @@ def scroll_to_widget(
34543459

34553460
# Adjust the region by the amount we just scrolled it, and convert to
34563461
# its parent's virtual coordinate system.
3457-
34583462
region = (
34593463
(
34603464
region.translate(-scroll_offset)
34613465
.translate(container.styles.margin.top_left)
34623466
.translate(container.styles.border.spacing.top_left)
3463-
.translate(-widget.scroll_offset)
34643467
.translate(container.virtual_region_with_margin.offset)
34653468
)
34663469
.grow(container.styles.margin)

0 commit comments

Comments
 (0)