Add alerts pill (#37821)

* add notification pill

* add update

* comment

* lfs upload

* rename

* fix

* try widget

* white is banned!

* same for bell

* need everywhere?

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
This commit is contained in:
Daniel Koepping
2026-04-14 12:02:30 -07:00
committed by GitHub
parent bf2294dee2
commit 4a15bdcdae
4 changed files with 55 additions and 4 deletions
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4ce1d357acadd798939b398cce1761ceb05564b44f2a5bc6865c7842e60e79f2
size 1474
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b3fe73cd1a24c05346a9b4a02e4f900a314c83a422beb38b0f88f91389582cd4
size 3960
+40 -3
View File
@@ -7,8 +7,8 @@ from collections.abc import Callable
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.layouts import HBoxLayout
from openpilot.system.ui.widgets.icon_widget import IconWidget
from openpilot.system.ui.widgets.label import UnifiedLabel
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
from openpilot.system.ui.widgets.label import UnifiedLabel, gui_label
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, DEFAULT_TEXT_COLOR
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.version import RELEASE_BRANCHES
@@ -28,6 +28,34 @@ NETWORK_TYPES = {
}
class AlertsPill(Widget):
def __init__(self):
super().__init__()
self.set_rect(rl.Rectangle(0, 0, 104, 52))
self._pill_bg_txt = gui_app.texture("icons_mici/alerts_pill.png", 104, 52)
self._bell_txt = gui_app.texture("icons_mici/alerts_bell.png", 28, 30)
self._alert_count_callback: Callable[[], int] | None = None
def set_alert_count_callback(self, callback: Callable[[], int] | None):
self._alert_count_callback = callback
def _render(self, _):
alert_count = self._alert_count_callback() if self._alert_count_callback else 0
if alert_count > 0:
pill_w, pill_h = self._pill_bg_txt.width, self._pill_bg_txt.height
rl.draw_texture_ex(self._pill_bg_txt, rl.Vector2(self.rect.x, self.rect.y), 0.0, 1.0, rl.WHITE)
bell_x = self.rect.x + 20
bell_y = self.rect.y + (pill_h - self._bell_txt.height) / 2
rl.draw_texture_ex(self._bell_txt, rl.Vector2(bell_x, bell_y), 0.0, 1.0, DEFAULT_TEXT_COLOR)
count_rect = rl.Rectangle(self.rect.x, self.rect.y, pill_w - 20, pill_h)
gui_label(count_rect, str(alert_count), font_size=36,
alignment=rl.GuiTextAlignment.TEXT_ALIGN_RIGHT,
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE)
class NetworkIcon(Widget):
def __init__(self):
super().__init__()
@@ -84,6 +112,7 @@ class MiciHomeLayout(Widget):
def __init__(self):
super().__init__()
self._on_settings_click: Callable | None = None
self._alert_count_callback: Callable[[], int] | None = None
self._last_refresh = 0
self._mouse_down_t: None | float = None
@@ -96,6 +125,8 @@ class MiciHomeLayout(Widget):
self._experimental_icon = IconWidget("icons_mici/experimental_mode.png", (48, 48))
self._mic_icon = IconWidget("icons_mici/microphone.png", (32, 46))
self._alerts_pill = AlertsPill()
self._status_bar_layout = HBoxLayout([
IconWidget("icons_mici/settings.png", (48, 48), opacity=0.9),
NetworkIcon(),
@@ -141,8 +172,9 @@ class MiciHomeLayout(Widget):
self._last_refresh = rl.get_time()
self._update_params()
def set_callbacks(self, on_settings: Callable | None = None):
def set_callbacks(self, on_settings: Callable | None = None, alert_count_callback: Callable[[], int] | None = None):
self._on_settings_click = on_settings
self._alerts_pill.set_alert_count_callback(alert_count_callback)
def _handle_mouse_release(self, mouse_pos: MousePos):
if not self._did_long_press:
@@ -203,3 +235,8 @@ class MiciHomeLayout(Widget):
footer_rect = rl.Rectangle(self.rect.x + HOME_PADDING, self.rect.y + self.rect.height - 48, self.rect.width - HOME_PADDING, 48)
self._status_bar_layout.render(footer_rect)
# TODO: add alignment to hboxlayout and add to there
self._alerts_pill.set_position(self.rect.x + self.rect.width - self._alerts_pill.rect.width - HOME_PADDING,
self.rect.y + self.rect.height - self._alerts_pill.rect.height)
self._alerts_pill.render()
+9 -1
View File
@@ -58,7 +58,10 @@ class MiciMainLayout(Scroller):
gui_app.push_widget(self._onboarding_window)
def _setup_callbacks(self):
self._home_layout.set_callbacks(on_settings=lambda: gui_app.push_widget(self._settings_layout))
self._home_layout.set_callbacks(
on_settings=lambda: gui_app.push_widget(self._settings_layout),
alert_count_callback=self._alerts_layout.active_alerts,
)
self._onroad_layout.set_click_callback(lambda: self._scroll_to(self._home_layout))
device.add_interactive_timeout_callback(self._on_interactive_timeout)
@@ -66,6 +69,11 @@ class MiciMainLayout(Scroller):
layout_x = int(layout.rect.x)
self._scroller.scroll_to(layout_x, smooth=True)
def _update_state(self):
super()._update_state()
# TODO: Hack to run alert updates while not in view. Add a nav stack tick?
self._alerts_layout._update_state()
def _render(self, _):
if not self._setup:
if self._alerts_layout.active_alerts() > 0: