diff --git a/selfdrive/ui/mici/layouts/main.py b/selfdrive/ui/mici/layouts/main.py index bd9167e82..2c3fea0d3 100644 --- a/selfdrive/ui/mici/layouts/main.py +++ b/selfdrive/ui/mici/layouts/main.py @@ -94,15 +94,13 @@ class MiciMainLayout(Scroller): # FIXME: these two pops can interrupt user interacting in the settings if self._onroad_time_delay is not None and rl.get_time() - self._onroad_time_delay >= ONROAD_DELAY: - gui_app.pop_widgets_to(self) - self._scroll_to(self._onroad_layout) + gui_app.pop_widgets_to(self, lambda: self._scroll_to(self._onroad_layout)) self._onroad_time_delay = None # When car leaves standstill, pop nav stack and scroll to onroad CS = ui_state.sm["carState"] if not CS.standstill and self._prev_standstill: - gui_app.pop_widgets_to(self) - self._scroll_to(self._onroad_layout) + gui_app.pop_widgets_to(self, lambda: self._scroll_to(self._onroad_layout)) self._prev_standstill = CS.standstill def _on_interactive_timeout(self): @@ -113,10 +111,10 @@ class MiciMainLayout(Scroller): if ui_state.started: # Don't pop if at standstill if not ui_state.sm["carState"].standstill: - gui_app.pop_widgets_to(self) - self._scroll_to(self._onroad_layout) + gui_app.pop_widgets_to(self, lambda: self._scroll_to(self._onroad_layout)) else: - gui_app.pop_widgets_to(self) + # Screen turns off on timeout offroad, so pop immediately without animation + gui_app.pop_widgets_to(self, instant=True) self._scroll_to(self._home_layout) def _on_bookmark_clicked(self): diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 34aed4f6a..98e05e112 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -383,27 +383,47 @@ class GuiApplication: self._nav_stack.append(widget) widget.show_event() + widget.set_enabled(True) - def pop_widget(self): + def pop_widget(self, idx: int | None = None): + # Pops widget instantly without animation if len(self._nav_stack) < 2: cloudlog.warning("At least one widget should remain on the stack, ignoring pop!") return - # re-enable previous widget and pop current - # TODO: switch to touch_valid - prev_widget = self._nav_stack[-2] - prev_widget.set_enabled(True) + idx_to_pop = len(self._nav_stack) - 1 if idx is None else idx + if idx_to_pop <= 0 or idx_to_pop >= len(self._nav_stack): + cloudlog.warning(f"Invalid index {idx_to_pop} to pop, ignoring!") + return - widget = self._nav_stack.pop() + # only re-enable previous widget if popping top widget + if idx_to_pop == len(self._nav_stack) - 1: + prev_widget = self._nav_stack[idx_to_pop - 1] + prev_widget.set_enabled(True) + + widget = self._nav_stack.pop(idx_to_pop) widget.hide_event() - def pop_widgets_to(self, widget): + def pop_widgets_to(self, widget: object, callback: Callable[[], None] | None = None, instant: bool = False): + # Pops middle widgets instantly without animation then dismisses top, animated out if NavWidget if widget not in self._nav_stack: cloudlog.warning("Widget not in stack, cannot pop to it!") return - # pops all widgets after specified widget - while len(self._nav_stack) > 0 and self._nav_stack[-1] != widget: + # Nothing to pop, ensure we still run callback + top_widget = self._nav_stack[-1] + if top_widget == widget: + if callback: + callback() + return + + # instantly pop widgets in between, then dismiss top widget for animation + while len(self._nav_stack) > 1 and self._nav_stack[-2] != widget: + self.pop_widget(len(self._nav_stack) - 2) + + if not instant: + top_widget.dismiss(callback) + else: self.pop_widget() def get_active_widget(self):