Scroller: add blocking scroll to (#37378)

* rename

* make tuple

* blocking
This commit is contained in:
Shane Smiskol
2026-02-24 00:50:47 -08:00
committed by GitHub
parent 081bb51e58
commit 3352e48c51
+23 -20
View File
@@ -78,8 +78,8 @@ class Scroller(Widget):
self._reset_scroll_at_show = True
self._scrolling_to: float | None = None
self._scroll_filter = FirstOrderFilter(0.0, SCROLL_RC, 1 / gui_app.target_fps)
self._scrolling_to: tuple[float | None, bool] = (None, False) # target offset, block user scrolling
self._scrolling_to_filter = FirstOrderFilter(0.0, SCROLL_RC, 1 / gui_app.target_fps)
self._zoom_filter = FirstOrderFilter(1.0, 0.2, 1 / gui_app.target_fps)
self._zoom_out_t: float = 0.0
@@ -115,7 +115,9 @@ class Scroller(Widget):
def set_reset_scroll_at_show(self, scroll: bool):
self._reset_scroll_at_show = scroll
def scroll_to(self, pos: float, smooth: bool = False):
def scroll_to(self, pos: float, smooth: bool = False, block: bool = False):
assert not block or smooth, "Instant scroll cannot be blocking"
# already there
if abs(pos) < 1:
return
@@ -123,13 +125,14 @@ class Scroller(Widget):
# FIXME: the padding correction doesn't seem correct
scroll_offset = self.scroll_panel.get_offset() - pos
if smooth:
self._scrolling_to = scroll_offset
self._scrolling_to_filter.x = self.scroll_panel.get_offset()
self._scrolling_to = scroll_offset, block
else:
self.scroll_panel.set_offset(scroll_offset)
@property
def is_auto_scrolling(self) -> bool:
return self._scrolling_to is not None
return self._scrolling_to[0] is not None
@property
def items(self) -> list[Widget]:
@@ -144,7 +147,7 @@ class Scroller(Widget):
# preserve original touch valid callback
original_touch_valid_callback = item._touch_valid_callback
item.set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid() and self.enabled and self._scrolling_to is None
item.set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid() and self.enabled and self._scrolling_to[0] is None
and not self.moving_items and (original_touch_valid_callback() if
original_touch_valid_callback else True))
@@ -154,7 +157,7 @@ class Scroller(Widget):
def _update_state(self):
if DO_ZOOM:
if self._scrolling_to is not None or self.scroll_panel.state != ScrollState.STEADY:
if self._scrolling_to[0] is not None or self.scroll_panel.state != ScrollState.STEADY:
self._zoom_out_t = rl.get_time() + MIN_ZOOM_ANIMATION_TIME
self._zoom_filter.update(0.85)
else:
@@ -164,24 +167,22 @@ class Scroller(Widget):
else:
self._zoom_filter.update(0.85)
# Cancel auto-scroll if user starts manually scrolling
if self._scrolling_to is not None and (self.scroll_panel.state == ScrollState.PRESSED or self.scroll_panel.state == ScrollState.MANUAL_SCROLL):
self._scrolling_to = None
# Cancel auto-scroll if user starts manually scrolling (unless blocking)
if (self.scroll_panel.state in (ScrollState.PRESSED, ScrollState.MANUAL_SCROLL) and
self._scrolling_to[0] is not None and not self._scrolling_to[1]):
self._scrolling_to = None, False
if self._scrolling_to is not None and len(self._pending_lift) == 0:
self._scroll_filter.update(self._scrolling_to)
self.scroll_panel.set_offset(self._scroll_filter.x)
if self._scrolling_to[0] is not None and len(self._pending_lift) == 0:
self._scrolling_to_filter.update(self._scrolling_to[0])
self.scroll_panel.set_offset(self._scrolling_to_filter.x)
if abs(self._scroll_filter.x - self._scrolling_to) < 1:
self.scroll_panel.set_offset(self._scrolling_to)
self._scrolling_to = None
else:
# keep current scroll position up to date
self._scroll_filter.x = self.scroll_panel.get_offset()
if abs(self._scrolling_to_filter.x - self._scrolling_to[0]) < 1:
self.scroll_panel.set_offset(self._scrolling_to[0])
self._scrolling_to = None, False
def _get_scroll(self, visible_items: list[Widget], content_size: float) -> float:
scroll_enabled = self._scroll_enabled() if callable(self._scroll_enabled) else self._scroll_enabled
self.scroll_panel.set_enabled(scroll_enabled and self.enabled)
self.scroll_panel.set_enabled(scroll_enabled and self.enabled and not self._scrolling_to[1])
self.scroll_panel.update(self._rect, content_size)
if not self._snap_items:
return round(self.scroll_panel.get_offset())
@@ -405,5 +406,7 @@ class Scroller(Widget):
self._move_lift.clear()
self._pending_lift.clear()
self._pending_move.clear()
self._scrolling_to = None, False
self._scrolling_to_filter.x = 0.0
for item in self._items:
item.hide_event()