scrollbar visibility rule

This commit is contained in:
Will McGugan
2025-10-05 11:27:25 +01:00
parent a77700c746
commit fec5571ee6
7 changed files with 27 additions and 8 deletions

View File

@@ -694,7 +694,7 @@ class Compositor:
if (
widget.show_vertical_scrollbar
or widget.show_horizontal_scrollbar
):
) and styles.scrollbar_visibility == "visible":
for chrome_widget, chrome_region in widget._arrange_scrollbars(
container_region
):

View File

@@ -52,6 +52,7 @@ from textual.css.constants import (
VALID_OVERLAY,
VALID_POSITION,
VALID_SCROLLBAR_GUTTER,
VALID_SCROLLBAR_VISIBILITY,
VALID_STYLE_FLAGS,
VALID_TEXT_ALIGN,
VALID_TEXT_OVERFLOW,
@@ -76,6 +77,7 @@ from textual.css.types import (
Display,
EdgeType,
Overflow,
ScrollbarVisibility,
TextOverflow,
TextWrap,
Visibility,
@@ -768,6 +770,13 @@ class StylesBuilder:
process_scrollbar_background_hover = process_color
process_scrollbar_background_active = process_color
def process_scrollbar_visibility(self, name: str, tokens: list[Token]) -> None:
"""Process scrollbar visibility rules."""
self.styles._rules["scrollbar_visibility"] = cast(
ScrollbarVisibility,
self._process_enum(name, tokens, VALID_SCROLLBAR_VISIBILITY),
)
process_link_color = process_color
process_link_background = process_color
process_link_color_hover = process_color

View File

@@ -90,6 +90,7 @@ VALID_HATCH: Final = {"left", "right", "cross", "vertical", "horizontal"}
VALID_TEXT_WRAP: Final = {"wrap", "nowrap"}
VALID_TEXT_OVERFLOW: Final = {"clip", "fold", "ellipsis"}
VALID_EXPAND: Final = {"greedy", "optimal"}
VALID_SCROLLBAR_VISIBILITY: Final = {"visible", "hidden"}
HATCHES: Final = {
"left": "",

View File

@@ -48,6 +48,7 @@ from textual.css.constants import (
VALID_OVERLAY,
VALID_POSITION,
VALID_SCROLLBAR_GUTTER,
VALID_SCROLLBAR_VISIBILITY,
VALID_TEXT_ALIGN,
VALID_TEXT_OVERFLOW,
VALID_TEXT_WRAP,
@@ -153,11 +154,10 @@ class RulesMap(TypedDict, total=False):
scrollbar_background: Color
scrollbar_background_hover: Color
scrollbar_background_active: Color
scrollbar_gutter: ScrollbarGutter
scrollbar_size_vertical: int
scrollbar_size_horizontal: int
scrollbar_visibility: ScrollbarVisibility
align_horizontal: AlignHorizontal
align_vertical: AlignVertical
@@ -242,6 +242,7 @@ class StylesBase:
"scrollbar_background",
"scrollbar_background_hover",
"scrollbar_background_active",
"scrollbar_visibility",
"link_color",
"link_background",
"link_color_hover",
@@ -424,6 +425,10 @@ class StylesBase:
"""Set the width of the vertical scrollbar (measured in cells)."""
scrollbar_size_horizontal = IntegerProperty(default=1, layout=True)
"""Set the height of the horizontal scrollbar (measured in cells)."""
scrollbar_visibility = StringEnumProperty(
VALID_SCROLLBAR_VISIBILITY, "visible", layout=True
)
"""Sets the visibility of the scrollbar."""
align_horizontal = StringEnumProperty(
VALID_ALIGN_HORIZONTAL, "left", layout=True, refresh_children=True
@@ -1153,6 +1158,8 @@ class Styles(StylesBase):
append_declaration(
"scrollbar-size-vertical", str(self.scrollbar_size_vertical)
)
if "scrollbar_visibility" in rules:
append_declaration("scrollbar-visibility", self.scrollbar_visibility)
if "box_sizing" in rules:
append_declaration("box-sizing", self.box_sizing)

View File

@@ -44,6 +44,7 @@ Position = Literal["relative", "absolute"]
TextWrap = Literal["wrap", "nowrap"]
TextOverflow = Literal["clip", "fold", "ellipsis"]
Expand = Literal["greedy", "expand"]
ScrollbarVisibility = Literal["visible", "hidden"]
Specificity3 = Tuple[int, int, int]
Specificity6 = Tuple[int, int, int, int, int, int]

View File

@@ -1723,8 +1723,6 @@ class DOMNode(MessagePump):
Should be called whenever CSS classes / pseudo classes change.
"""
if not self.is_attached or not self.screen.is_mounted:
return
try:
self.app.update_styles(self)
except NoActiveAppError:

View File

@@ -346,7 +346,7 @@ class Widget(DOMNode):
loading: Reactive[bool] = Reactive(False)
"""If set to `True` this widget will temporarily be replaced with a loading indicator."""
virtual_size: Reactive[Size] = Reactive(Size(0, 0), layout=True)
virtual_size: Reactive[Size] = Reactive(Size(0, 0), layout=True, repaint=False)
"""The virtual (scrollable) [size][textual.geometry.Size] of the widget."""
has_focus: Reactive[bool] = Reactive(False, repaint=False)
@@ -3421,6 +3421,7 @@ class Widget(DOMNode):
`True` if any scrolling has occurred in any descendant, otherwise `False`.
"""
# Grow the region by the margin so to keep the margin in view.
region = widget.virtual_region_with_margin
scrolled = False
@@ -3430,7 +3431,11 @@ class Widget(DOMNode):
return False
while isinstance(widget.parent, Widget) and widget is not self:
if not region:
break
container = widget.parent
if widget.styles.dock != "none":
scroll_offset = Offset(0, 0)
else:
@@ -3454,13 +3459,11 @@ class Widget(DOMNode):
# Adjust the region by the amount we just scrolled it, and convert to
# its parent's virtual coordinate system.
region = (
(
region.translate(-scroll_offset)
.translate(container.styles.margin.top_left)
.translate(container.styles.border.spacing.top_left)
.translate(-widget.scroll_offset)
.translate(container.virtual_region_with_margin.offset)
)
.grow(container.styles.margin)