From 0723c8b23e7a5011565698a8391dae6c3c31da83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Wed, 18 Jan 2023 18:25:00 +0000
Subject: [PATCH 01/11] Fix #1607 for generic/integer properties.
---
src/textual/css/_style_properties.py | 17 ++++++++++++++---
src/textual/css/styles.py | 8 ++++----
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
index bfc9f90cb..81ab274a4 100644
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -60,9 +60,20 @@ PropertySetType = TypeVar("PropertySetType")
class GenericProperty(Generic[PropertyGetType, PropertySetType]):
- def __init__(self, default: PropertyGetType, layout: bool = False) -> None:
+ """Generic property to be inherited by other style properties.
+
+ Args:
+ default: The default value (or a factory thereof) of the property.
+ layout: Whether to refresh the node layout on value change.
+ children: Whether to refresh the node children on value change.
+ """
+
+ def __init__(
+ self, default: PropertyGetType, layout: bool = False, children: bool = False
+ ) -> None:
self.default = default
self.layout = layout
+ self.children = children
def validate_value(self, value: object) -> PropertyGetType:
"""Validate the setter value.
@@ -88,11 +99,11 @@ class GenericProperty(Generic[PropertyGetType, PropertySetType]):
_rich_traceback_omit = True
if value is None:
obj.clear_rule(self.name)
- obj.refresh(layout=self.layout)
+ obj.refresh(layout=self.layout, children=self.children)
return
new_value = self.validate_value(value)
if obj.set_rule(self.name, new_value):
- obj.refresh(layout=self.layout)
+ obj.refresh(layout=self.layout, children=self.children)
class IntegerProperty(GenericProperty[int, int]):
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index c04b24a48..a9903da99 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -280,10 +280,10 @@ class StylesBase(ABC):
grid_rows = ScalarListProperty()
grid_columns = ScalarListProperty()
- grid_size_columns = IntegerProperty(default=1, layout=True)
- grid_size_rows = IntegerProperty(default=0, layout=True)
- grid_gutter_horizontal = IntegerProperty(default=0, layout=True)
- grid_gutter_vertical = IntegerProperty(default=0, layout=True)
+ grid_size_columns = IntegerProperty(default=1, layout=True, children=True)
+ grid_size_rows = IntegerProperty(default=0, layout=True, children=True)
+ grid_gutter_horizontal = IntegerProperty(default=0, layout=True, children=True)
+ grid_gutter_vertical = IntegerProperty(default=0, layout=True, children=True)
row_span = IntegerProperty(default=1, layout=True)
column_span = IntegerProperty(default=1, layout=True)
From e6c23f5d0f8276d3a043c951016908c21cde4068 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Wed, 18 Jan 2023 18:33:57 +0000
Subject: [PATCH 02/11] Fix #1607 for ScalarListProperty.
---
src/textual/css/_style_properties.py | 15 ++++++++++++---
src/textual/css/styles.py | 4 ++--
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
index 81ab274a4..3805ffb18 100644
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -60,7 +60,7 @@ PropertySetType = TypeVar("PropertySetType")
class GenericProperty(Generic[PropertyGetType, PropertySetType]):
- """Generic property to be inherited by other style properties.
+ """Descriptor that abstracts away common machinery for other style descriptors.
Args:
default: The default value (or a factory thereof) of the property.
@@ -213,6 +213,15 @@ class ScalarProperty:
class ScalarListProperty:
+ """Descriptor for lists of scalars.
+
+ Args:
+ children: Whether to refresh the node children on value change.
+ """
+
+ def __init__(self, children: bool = False) -> None:
+ self.children = children
+
def __set_name__(self, owner: Styles, name: str) -> None:
self.name = name
@@ -226,7 +235,7 @@ class ScalarListProperty:
) -> None:
if value is None:
obj.clear_rule(self.name)
- obj.refresh(layout=True)
+ obj.refresh(layout=True, children=self.children)
return
parse_values: Iterable[str | float]
if isinstance(value, str):
@@ -245,7 +254,7 @@ class ScalarListProperty:
else parse_value
)
if obj.set_rule(self.name, tuple(scalars)):
- obj.refresh(layout=True)
+ obj.refresh(layout=True, children=self.children)
class BoxProperty:
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index a9903da99..feb39adcc 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -277,8 +277,8 @@ class StylesBase(ABC):
content_align_vertical = StringEnumProperty(VALID_ALIGN_VERTICAL, "top")
content_align = AlignProperty()
- grid_rows = ScalarListProperty()
- grid_columns = ScalarListProperty()
+ grid_rows = ScalarListProperty(children=True)
+ grid_columns = ScalarListProperty(children=True)
grid_size_columns = IntegerProperty(default=1, layout=True, children=True)
grid_size_rows = IntegerProperty(default=0, layout=True, children=True)
From 6c5022a78ed9770062ec36810514d3b79e1ef768 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Wed, 18 Jan 2023 18:48:21 +0000
Subject: [PATCH 03/11] Fix #1607 for some StringEnumProperty.
This commit fixes the issue for scrollbar_gutter, align, align_horizontal, and align_vertical, but it doesn't solve the issue for overflow_x/y, and I still don't know why...
[skip ci]
---
src/textual/css/_style_properties.py | 19 ++++++++++++++++---
src/textual/css/styles.py | 12 +++++++++---
2 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
index 3805ffb18..2d05b3b50 100644
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -699,12 +699,25 @@ class OffsetProperty:
class StringEnumProperty:
"""Descriptor for getting and setting string properties and ensuring that the set
value belongs in the set of valid values.
+
+ Args:
+ valid_values: The set of valid values that the descriptor can take.
+ default: The default value (or a factory thereof) of the property.
+ layout: Whether to refresh the node layout on value change.
+ children: Whether to refresh the node children on value change.
"""
- def __init__(self, valid_values: set[str], default: str, layout=False) -> None:
+ def __init__(
+ self,
+ valid_values: set[str],
+ default: str,
+ layout: bool = False,
+ children: bool = False,
+ ) -> None:
self._valid_values = valid_values
self._default = default
self._layout = layout
+ self._children = children
def __set_name__(self, owner: StylesBase, name: str) -> None:
self.name = name
@@ -734,7 +747,7 @@ class StringEnumProperty:
_rich_traceback_omit = True
if value is None:
if obj.clear_rule(self.name):
- obj.refresh(layout=self._layout)
+ obj.refresh(layout=self._layout, children=self._children)
else:
if value not in self._valid_values:
raise StyleValueError(
@@ -746,7 +759,7 @@ class StringEnumProperty:
),
)
if obj.set_rule(self.name, value):
- obj.refresh(layout=self._layout)
+ obj.refresh(layout=self._layout, children=self._children)
class NameProperty:
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index feb39adcc..6f94b1877 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -264,13 +264,19 @@ class StylesBase(ABC):
scrollbar_background_hover = ColorProperty("#444444")
scrollbar_background_active = ColorProperty("black")
- scrollbar_gutter = StringEnumProperty(VALID_SCROLLBAR_GUTTER, "auto")
+ scrollbar_gutter = StringEnumProperty(
+ VALID_SCROLLBAR_GUTTER, "auto", layout=True, children=True
+ )
scrollbar_size_vertical = IntegerProperty(default=1, layout=True)
scrollbar_size_horizontal = IntegerProperty(default=1, layout=True)
- align_horizontal = StringEnumProperty(VALID_ALIGN_HORIZONTAL, "left")
- align_vertical = StringEnumProperty(VALID_ALIGN_VERTICAL, "top")
+ align_horizontal = StringEnumProperty(
+ VALID_ALIGN_HORIZONTAL, "left", layout=True, children=True
+ )
+ align_vertical = StringEnumProperty(
+ VALID_ALIGN_VERTICAL, "top", layout=True, children=True
+ )
align = AlignProperty()
content_align_horizontal = StringEnumProperty(VALID_ALIGN_HORIZONTAL, "left")
From 19780db0bb50b86371cbf8ec09ed5e2ab50132f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Thu, 19 Jan 2023 15:29:09 +0000
Subject: [PATCH 04/11] Setting overflow refreshes layout.
[skip-ci]
---
src/textual/css/styles.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index 6f94b1877..83e1bcd30 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -246,8 +246,12 @@ class StylesBase(ABC):
dock = DockProperty()
- overflow_x = StringEnumProperty(VALID_OVERFLOW, "hidden")
- overflow_y = StringEnumProperty(VALID_OVERFLOW, "hidden")
+ overflow_x = StringEnumProperty(
+ VALID_OVERFLOW, "hidden", layout=True, children=True
+ )
+ overflow_y = StringEnumProperty(
+ VALID_OVERFLOW, "hidden", layout=True, children=True
+ )
layer = NameProperty()
layers = NameListProperty()
From ef4bd43c9bf0014ca626ba4951f986ff2a9ebc8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Fri, 20 Jan 2023 11:33:30 +0000
Subject: [PATCH 05/11] Add regression test.
---
tests/css/test_programmatic_style_changes.py | 64 ++++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 tests/css/test_programmatic_style_changes.py
diff --git a/tests/css/test_programmatic_style_changes.py b/tests/css/test_programmatic_style_changes.py
new file mode 100644
index 000000000..3abc15ba1
--- /dev/null
+++ b/tests/css/test_programmatic_style_changes.py
@@ -0,0 +1,64 @@
+import pytest
+
+from textual.app import App
+from textual.containers import Grid
+from textual.screen import Screen
+from textual.widgets import Label
+
+
+updates = 0
+
+
+class _Label(Label):
+ """Label widget that keeps track of its own updates."""
+
+ def refresh(self, *args, **kwargs):
+ global updates
+ updates += 1
+ return super().refresh(*args, **kwargs)
+
+
+@pytest.mark.parametrize(
+ "style, value",
+ [
+ ("grid_size_rows", 2),
+ ("grid_size_columns", 2),
+ ("grid_gutter_vertical", 2),
+ ("grid_gutter_horizontal", 1),
+ ("grid_rows", "1fr 3fr"),
+ ("grid_columns", "1fr 3fr"),
+ ("overflow_x", "scroll"),
+ ("overflow_y", "scroll"),
+ ("scrollbar_gutter", "stable"),
+ ("align_horizontal", "right"),
+ ("align_vertical", "bottom"),
+ ("align", ("right", "bottom")),
+ ],
+)
+def test_programmatic_style_change_refreshes_children_layout(style: str, value):
+ """Regression test for #1607 https://github.com/Textualize/textual/issues/1607
+
+ Some programmatic style changes to a widget were not updating the layout of the
+ children widgets, which seemed to be happening when the style change did not affect
+ the size of the widget but did affect the layout of the children.
+ """
+
+ global updates
+
+ app = App()
+ app.DEFAULT_CSS = "Grid { grid-size: 1 1; }"
+ app._set_active()
+ app.push_screen(Screen())
+
+ grid = Grid(
+ _Label("one"),
+ _Label("two"),
+ _Label("three"),
+ _Label("four"),
+ )
+ app.screen._add_children(grid)
+
+ update_count = updates
+ setattr(grid.styles, style, value)
+ print(updates, update_count)
+ assert updates > update_count
From c7f1771bbf91a36e6b8aa84c3dbe29df09912191 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Fri, 20 Jan 2023 11:35:14 +0000
Subject: [PATCH 06/11] Update changelog.
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3ffc3dbe..8794ceda5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- Fixed `textual diagnose` crash on older supported Python versions. https://github.com/Textualize/textual/issues/1622
+- Fixed programmatic style changes not refreshing children layouts when parent widget did not change size https://github.com/Textualize/textual/issues/1607
### Changed
From 3305659132ccdbcd1e4fb894025b391156bb07e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Mon, 23 Jan 2023 17:41:19 +0000
Subject: [PATCH 07/11] Fix silly mistake when resolving merge conflict.
---
src/textual/css/_style_properties.py | 4 ++--
src/textual/css/styles.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
index e91282af3..10c8f9f8a 100644
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -220,9 +220,9 @@ class ScalarListProperty:
percent_unit: The dimension to which percentage scalars will be relative to.
"""
- def __init__(self, children: bool = False, percent_unit: Unit) -> None:
- self.children = children
+ def __init__(self, percent_unit: Unit, children: bool = False) -> None:
self.percent_unit = percent_unit
+ self.children = children
def __set_name__(self, owner: Styles, name: str) -> None:
self.name = name
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index 29cacf7cb..4a3691bcc 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -287,8 +287,8 @@ class StylesBase(ABC):
content_align_vertical = StringEnumProperty(VALID_ALIGN_VERTICAL, "top")
content_align = AlignProperty()
- grid_rows = ScalarListProperty(children=True, percent_unit=Unit.HEIGHT)
- grid_columns = ScalarListProperty(children=True, percent_unit=Unit.WIDTH)
+ grid_rows = ScalarListProperty(percent_unit=Unit.HEIGHT, children=True)
+ grid_columns = ScalarListProperty(percent_unit=Unit.WIDTH, children=True)
grid_size_columns = IntegerProperty(default=1, layout=True, children=True)
grid_size_rows = IntegerProperty(default=0, layout=True, children=True)
From 3f2ecd1ad863e0fa6f243fb8365726289595d26a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Mon, 23 Jan 2023 18:11:33 +0000
Subject: [PATCH 08/11] Update changelog.
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e4f19ef8..2cac4c421 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- Fixed stuck screen https://github.com/Textualize/textual/issues/1632
+- Fixed programmatic style changes not refreshing children layouts when parent widget did not change size https://github.com/Textualize/textual/issues/1607
- Fixed relative units in `grid-rows` and `grid-columns` being computed with respect to the wrong dimension https://github.com/Textualize/textual/issues/1406
## [0.10.1] - 2023-01-20
@@ -21,7 +22,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- Fixed `textual diagnose` crash on older supported Python versions. https://github.com/Textualize/textual/issues/1622
-- Fixed programmatic style changes not refreshing children layouts when parent widget did not change size https://github.com/Textualize/textual/issues/1607
### Changed
From c4dbde19948d1c0f162594871c1f5f7bf0a7e7de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Thu, 26 Jan 2023 16:14:24 +0000
Subject: [PATCH 09/11] Don't force layout on overflow change.
Fix: #1628.
---
src/textual/css/styles.py | 4 ++--
tests/css/test_programmatic_style_changes.py | 2 --
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index cc9e99a6a..a7021a5b8 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -247,8 +247,8 @@ class StylesBase(ABC):
dock = DockProperty()
- overflow_x = OverflowProperty(VALID_OVERFLOW, "hidden", layout=True, children=True)
- overflow_y = OverflowProperty(VALID_OVERFLOW, "hidden", layout=True, children=True)
+ overflow_x = OverflowProperty(VALID_OVERFLOW, "hidden")
+ overflow_y = OverflowProperty(VALID_OVERFLOW, "hidden")
layer = NameProperty()
layers = NameListProperty()
diff --git a/tests/css/test_programmatic_style_changes.py b/tests/css/test_programmatic_style_changes.py
index 3abc15ba1..b30f34ba7 100644
--- a/tests/css/test_programmatic_style_changes.py
+++ b/tests/css/test_programmatic_style_changes.py
@@ -27,8 +27,6 @@ class _Label(Label):
("grid_gutter_horizontal", 1),
("grid_rows", "1fr 3fr"),
("grid_columns", "1fr 3fr"),
- ("overflow_x", "scroll"),
- ("overflow_y", "scroll"),
("scrollbar_gutter", "stable"),
("align_horizontal", "right"),
("align_vertical", "bottom"),
From b023d4e02e0a91c99847da4a6429c008ae3ec398 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?=
<5621605+rodrigogiraoserrao@users.noreply.github.com>
Date: Mon, 30 Jan 2023 11:50:02 +0000
Subject: [PATCH 10/11] Improve tests.
---
tests/css/test_programmatic_style_changes.py | 107 +++++++-----
.../__snapshots__/test_snapshots.ambr | 155 ++++++++++++++++++
.../programmatic_scrollbar_gutter_change.py | 29 ++++
tests/snapshot_tests/test_snapshots.py | 10 ++
4 files changed, 264 insertions(+), 37 deletions(-)
create mode 100644 tests/snapshot_tests/snapshot_apps/programmatic_scrollbar_gutter_change.py
diff --git a/tests/css/test_programmatic_style_changes.py b/tests/css/test_programmatic_style_changes.py
index b30f34ba7..f15ecdd55 100644
--- a/tests/css/test_programmatic_style_changes.py
+++ b/tests/css/test_programmatic_style_changes.py
@@ -6,57 +6,90 @@ from textual.screen import Screen
from textual.widgets import Label
-updates = 0
-
-
-class _Label(Label):
- """Label widget that keeps track of its own updates."""
-
- def refresh(self, *args, **kwargs):
- global updates
- updates += 1
- return super().refresh(*args, **kwargs)
-
-
@pytest.mark.parametrize(
"style, value",
[
- ("grid_size_rows", 2),
- ("grid_size_columns", 2),
- ("grid_gutter_vertical", 2),
- ("grid_gutter_horizontal", 1),
+ ("grid_size_rows", 3),
+ ("grid_size_columns", 3),
+ ("grid_gutter_vertical", 4),
+ ("grid_gutter_horizontal", 4),
("grid_rows", "1fr 3fr"),
("grid_columns", "1fr 3fr"),
- ("scrollbar_gutter", "stable"),
- ("align_horizontal", "right"),
- ("align_vertical", "bottom"),
- ("align", ("right", "bottom")),
],
)
-def test_programmatic_style_change_refreshes_children_layout(style: str, value):
+async def test_programmatic_style_change_updates_children(style: str, value: object):
"""Regression test for #1607 https://github.com/Textualize/textual/issues/1607
Some programmatic style changes to a widget were not updating the layout of the
children widgets, which seemed to be happening when the style change did not affect
the size of the widget but did affect the layout of the children.
+
+ This test, in particular, checks the attributes that _should_ affect the size of the
+ children widgets.
"""
- global updates
+ class MyApp(App[None]):
+ CSS = """
+ Grid { grid-size: 2 2; }
+ Label { width: 100%; height: 100%; }
+ """
- app = App()
- app.DEFAULT_CSS = "Grid { grid-size: 1 1; }"
- app._set_active()
- app.push_screen(Screen())
+ def compose(self):
+ yield Grid(
+ Label("one"),
+ Label("two"),
+ Label("three"),
+ Label("four"),
+ )
- grid = Grid(
- _Label("one"),
- _Label("two"),
- _Label("three"),
- _Label("four"),
- )
- app.screen._add_children(grid)
+ app = MyApp()
- update_count = updates
- setattr(grid.styles, style, value)
- print(updates, update_count)
- assert updates > update_count
+ async with app.run_test() as pilot:
+ sizes = [(lbl.size.width, lbl.size.height) for lbl in app.screen.query(Label)]
+
+ setattr(app.query_one(Grid).styles, style, value)
+ await pilot.pause()
+
+ assert sizes != [
+ (lbl.size.width, lbl.size.height) for lbl in app.screen.query(Label)
+ ]
+
+
+@pytest.mark.parametrize(
+ "style, value",
+ [
+ ("align_horizontal", "right"),
+ ("align_vertical", "bottom"),
+ ("align", ("right", "bottom")),
+ ],
+)
+async def test_programmatic_align_change_updates_children_position(
+ style: str, value: str
+):
+ """Regression test for #1607 for the align(_xxx) styles.
+
+ See https://github.com/Textualize/textual/issues/1607.
+ """
+
+ class MyApp(App[None]):
+ CSS = "Grid { grid-size: 2 2; }"
+
+ def compose(self):
+ yield Grid(
+ Label("one"),
+ Label("two"),
+ Label("three"),
+ Label("four"),
+ )
+
+ app = MyApp()
+
+ async with app.run_test() as pilot:
+ offsets = [(lbl.region.x, lbl.region.y) for lbl in app.screen.query(Label)]
+
+ setattr(app.query_one(Grid).styles, style, value)
+ await pilot.pause()
+
+ assert offsets != [
+ (lbl.region.x, lbl.region.y) for lbl in app.screen.query(Label)
+ ]
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index 52b617982..27dfb81aa 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -13651,6 +13651,161 @@
'''
# ---
+# name: test_programmatic_scrollbar_gutter_change
+ '''
+
+
+ '''
+# ---
# name: test_textlog_max_lines
'''