diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 320e7477f1..c33b16fac4 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -41,6 +41,8 @@ class HomeLayout(Widget): self.update_available = False self.alert_count = 0 + self._prev_update_available = False + self._prev_alerts_present = False self.header_rect = rl.Rectangle(0, 0, 0, 0) self.content_rect = rl.Rectangle(0, 0, 0, 0) @@ -185,20 +187,23 @@ class HomeLayout(Widget): def _refresh(self): # TODO: implement _update_state with a timer - self.update_available = self.update_alert.refresh() - self.alert_count = self.offroad_alert.refresh() - self._update_state_priority(self.update_available, self.alert_count > 0) - - def _update_state_priority(self, update_available: bool, alerts_present: bool): - current_state = self.current_state + update_available = self.update_alert.refresh() + alert_count = self.offroad_alert.refresh() + alerts_present = alert_count > 0 + # 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 - elif update_available and (current_state == HomeLayoutState.HOME or (not alerts_present and current_state == HomeLayoutState.ALERTS)): + elif update_available and ((not self._prev_update_available) or (not alerts_present and self.current_state == HomeLayoutState.ALERTS)): self.current_state = HomeLayoutState.UPDATE - elif alerts_present and (current_state == HomeLayoutState.HOME or (not update_available and current_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.update_available = update_available + self.alert_count = alert_count + self._prev_update_available = update_available + self._prev_alerts_present = alerts_present + def _get_version_text(self) -> str: brand = "openpilot" description = self.params.get("UpdaterCurrentDescription") diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 1ac5083fc6..81eef60d69 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -70,6 +70,7 @@ def setup_pair_device(click, pm: PubMaster): def setup_offroad_alert(click, pm: PubMaster): set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C') + set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal') for alert in OFFROAD_ALERTS: set_offroad_alert(alert, True) diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 7ad96bc9a5..586d90dc4b 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -29,9 +29,10 @@ class AlertConstants: MARGIN = 50 SPACING = 30 FONT_SIZE = 48 - BORDER_RADIUS = 30 + BORDER_RADIUS = 30 * 2 # matches Qt's 30px ALERT_HEIGHT = 120 - ALERT_SPACING = 20 + ALERT_SPACING = 10 + ALERT_INSET = 60 @dataclass @@ -88,7 +89,7 @@ class AbstractAlert(Widget, ABC): HARDWARE.reboot() def _render(self, rect: rl.Rectangle): - rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.width, 10, AlertColors.BACKGROUND) + rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.height, 10, AlertColors.BACKGROUND) footer_height = AlertConstants.BUTTON_SIZE[1] + AlertConstants.SPACING content_height = rect.height - 2 * AlertConstants.MARGIN - footer_height @@ -138,7 +139,8 @@ class AbstractAlert(Widget, ABC): self.dismiss_btn_rect.x = rect.x + AlertConstants.MARGIN self.dismiss_btn_rect.y = footer_y - rl.draw_rectangle_rounded(self.dismiss_btn_rect, 0.3, 10, AlertColors.BUTTON) + roundness = AlertConstants.BORDER_RADIUS / self.dismiss_btn_rect.height + rl.draw_rectangle_rounded(self.dismiss_btn_rect, roundness, 10, AlertColors.BUTTON) text = "Close" text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x @@ -151,7 +153,8 @@ class AbstractAlert(Widget, ABC): if self.snooze_visible: self.snooze_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.SNOOZE_BUTTON_SIZE[0] self.snooze_btn_rect.y = footer_y - rl.draw_rectangle_rounded(self.snooze_btn_rect, 0.3, 10, AlertColors.SNOOZE_BG) + roundness = AlertConstants.BORDER_RADIUS / self.snooze_btn_rect.height + rl.draw_rectangle_rounded(self.snooze_btn_rect, roundness, 10, AlertColors.SNOOZE_BG) text = "Snooze Update" text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x @@ -162,7 +165,8 @@ class AbstractAlert(Widget, ABC): elif self.has_reboot_btn: self.reboot_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.REBOOT_BUTTON_SIZE[0] self.reboot_btn_rect.y = footer_y - rl.draw_rectangle_rounded(self.reboot_btn_rect, 0.3, 10, AlertColors.BUTTON) + roundness = AlertConstants.BORDER_RADIUS / self.reboot_btn_rect.height + rl.draw_rectangle_rounded(self.reboot_btn_rect, roundness, 10, AlertColors.BUTTON) text = "Reboot and Update" text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x @@ -215,11 +219,11 @@ class OffroadAlert(AbstractAlert): if not alert_data.visible: continue - text_width = int(self.content_rect.width - 90) + text_width = int(self.content_rect.width - (AlertConstants.ALERT_INSET * 2)) wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) line_count = len(wrapped_lines) text_height = line_count * (AlertConstants.FONT_SIZE + 5) - alert_item_height = max(text_height + 40, AlertConstants.ALERT_HEIGHT) + alert_item_height = max(text_height + (AlertConstants.ALERT_INSET * 2), AlertConstants.ALERT_HEIGHT) total_height += alert_item_height + AlertConstants.ALERT_SPACING if total_height > 20: @@ -235,7 +239,7 @@ class OffroadAlert(AbstractAlert): self.sorted_alerts.append(alert_data) def _render_content(self, content_rect: rl.Rectangle): - y_offset = 20 + y_offset = AlertConstants.ALERT_SPACING font = gui_app.font(FontWeight.NORMAL) for alert_data in self.sorted_alerts: @@ -243,11 +247,11 @@ class OffroadAlert(AbstractAlert): continue bg_color = AlertColors.HIGH_SEVERITY if alert_data.severity > 0 else AlertColors.LOW_SEVERITY - text_width = int(content_rect.width - 90) + text_width = int(content_rect.width - (AlertConstants.ALERT_INSET * 2)) wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) line_count = len(wrapped_lines) text_height = line_count * (AlertConstants.FONT_SIZE + 5) - alert_item_height = max(text_height + 40, AlertConstants.ALERT_HEIGHT) + alert_item_height = max(text_height + (AlertConstants.ALERT_INSET * 2), AlertConstants.ALERT_HEIGHT) alert_rect = rl.Rectangle( content_rect.x + 10, @@ -256,10 +260,11 @@ class OffroadAlert(AbstractAlert): alert_item_height, ) - rl.draw_rectangle_rounded(alert_rect, 0.2, 10, bg_color) + roundness = AlertConstants.BORDER_RADIUS / min(alert_rect.height, alert_rect.width) + rl.draw_rectangle_rounded(alert_rect, roundness, 10, bg_color) - text_x = alert_rect.x + 30 - text_y = alert_rect.y + 20 + text_x = alert_rect.x + AlertConstants.ALERT_INSET + text_y = alert_rect.y + AlertConstants.ALERT_INSET for i, line in enumerate(wrapped_lines): rl.draw_text_ex(