From fdcf8b592e5596386c5639875ce260b19267cc2f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 21:07:36 -0700 Subject: [PATCH] raylib: reset scrollers and description expansions on show event (#36304) * scroll up on hide * switch to show * dismiss descriptions too! * all is show * all is show * clean up * visible items helper * Revert "visible items helper" This reverts commit e64f05b69155483aa0f3d74bd511f5d7c1ecfb79. * reset --- selfdrive/ui/layouts/home.py | 15 ++++++++++++--- selfdrive/ui/layouts/settings/developer.py | 1 + selfdrive/ui/layouts/settings/device.py | 3 +++ selfdrive/ui/layouts/settings/firehose.py | 3 +++ selfdrive/ui/layouts/settings/software.py | 3 +++ selfdrive/ui/layouts/settings/toggles.py | 1 + selfdrive/ui/widgets/offroad_alerts.py | 3 +++ system/ui/lib/scroll_panel.py | 5 +++++ system/ui/widgets/list_view.py | 10 ++++++++-- system/ui/widgets/scroller.py | 12 ++++++++++++ 10 files changed, 51 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index f7f57c680d..9243d8ea1f 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -36,6 +36,8 @@ class HomeLayout(Widget): self.update_alert = UpdateAlert() self.offroad_alert = OffroadAlert() + self._layout_widgets = {HomeLayoutState.UPDATE: self.update_alert, HomeLayoutState.ALERTS: self.offroad_alert} + self.current_state = HomeLayoutState.HOME self.last_refresh = 0 self.settings_callback: callable | None = None @@ -71,6 +73,13 @@ class HomeLayout(Widget): self.settings_callback = callback def _set_state(self, state: HomeLayoutState): + # propagate show/hide events + if state != self.current_state: + if state in self._layout_widgets: + self._layout_widgets[state].show_event() + if self.current_state in self._layout_widgets: + self._layout_widgets[self.current_state].hide_event() + self.current_state = state def _render(self, rect: rl.Rectangle): @@ -201,11 +210,11 @@ class HomeLayout(Widget): # Show panels on transition from no alert/update to any alerts/update if not update_available and not alerts_present: - self.current_state = HomeLayoutState.HOME + self._set_state(HomeLayoutState.HOME) elif update_available and ((not self._prev_update_available) or (not alerts_present and self.current_state == HomeLayoutState.ALERTS)): - self.current_state = HomeLayoutState.UPDATE + self._set_state(HomeLayoutState.UPDATE) elif alerts_present and ((not self._prev_alerts_present) or (not update_available and self.current_state == HomeLayoutState.UPDATE)): - self.current_state = HomeLayoutState.ALERTS + self._set_state(HomeLayoutState.ALERTS) self.update_available = update_available self.alert_count = alert_count diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index d3a829df81..bc8fc953c4 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -88,6 +88,7 @@ class DeveloperLayout(Widget): self._scroller.render(rect) def show_event(self): + self._scroller.show_event() self._update_toggles() def _update_toggles(self): diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 758200c0ad..286ba36f07 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -66,6 +66,9 @@ class DeviceLayout(Widget): regulatory_btn.set_visible(TICI) return items + def show_event(self): + self._scroller.show_event() + def _render(self, rect): self._scroller.render(rect) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index 7e1d3b61ba..353a9d976f 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -49,6 +49,9 @@ class FirehoseLayout(Widget): self.update_thread.start() self.last_update_time = 0 + def show_event(self): + self.scroll_panel.set_offset(0) + def _get_segment_count(self) -> int: stats = self.params.get(self.PARAM_KEY) if not stats: diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 7aebc609f8..a337b9034d 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -69,6 +69,9 @@ class SoftwareLayout(Widget): ] return items + def show_event(self): + self._scroller.show_event() + def _render(self, rect): self._scroller.render(rect) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 8fdbd6f9a6..c75cc7574d 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -144,6 +144,7 @@ class TogglesLayout(Widget): ui_state.personality = personality def show_event(self): + self._scroller.show_event() self._update_toggles() def _update_toggles(self): diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 1045971636..49edafa0f1 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -117,6 +117,9 @@ class AbstractAlert(Widget, ABC): self.scroll_panel_rect = rl.Rectangle(0, 0, 0, 0) self.scroll_panel = GuiScrollPanel() + def show_event(self): + self.scroll_panel.set_offset(0) + def set_dismiss_callback(self, callback: Callable): self.dismiss_callback = callback self.dismiss_btn.set_click_callback(self.dismiss_callback) diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index d0d59df1ff..f0031138fe 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -123,3 +123,8 @@ class GuiScrollPanel: def is_touch_valid(self): return self._scroll_state == ScrollState.IDLE and abs(self._velocity_filter_y.x) < MIN_VELOCITY_FOR_CLICKING + + def set_offset(self, position: float) -> None: + self._offset_filter_y.x = position + self._velocity_filter_y.x = 0.0 + self._scroll_state = ScrollState.IDLE diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index d203ec8139..b1c4b44df1 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -281,6 +281,9 @@ class ListItem(Widget): # Cached properties for performance self._prev_description: str | None = self.description + def show_event(self): + self._set_description_visible(False) + def set_description_opened_callback(self, callback: Callable) -> None: self.description_opened_callback = callback @@ -304,8 +307,11 @@ class ListItem(Widget): # Click was on right item, don't toggle description return - if self.description: - self.description_visible = not self.description_visible + self._set_description_visible(not self.description_visible) + + def _set_description_visible(self, visible: bool): + if self.description and self.description_visible != visible: + self.description_visible = visible # do callback first in case receiver changes description if self.description_visible and self.description_opened_callback is not None: self.description_opened_callback() diff --git a/system/ui/widgets/scroller.py b/system/ui/widgets/scroller.py index c76f30d196..f19a6fbfdb 100644 --- a/system/ui/widgets/scroller.py +++ b/system/ui/widgets/scroller.py @@ -76,3 +76,15 @@ class Scroller(Widget): item.render() rl.end_scissor_mode() + + def show_event(self): + super().show_event() + # Reset to top + self.scroll_panel.set_offset(0) + for item in self._items: + item.show_event() + + def hide_event(self): + super().hide_event() + for item in self._items: + item.hide_event()