diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 5f6faddbf2..a7cad7c6ec 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -6,6 +6,7 @@ from openpilot.common.params import Params from openpilot.selfdrive.ui.widgets.offroad_alerts import UpdateAlert, OffroadAlert from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButton from openpilot.selfdrive.ui.widgets.prime import PrimeWidget +from openpilot.selfdrive.ui.widgets.setup import SetupWidget from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR, Widget @@ -49,7 +50,8 @@ class HomeLayout(Widget): self.update_notif_rect = rl.Rectangle(0, 0, 200, HEADER_HEIGHT - 10) self.alert_notif_rect = rl.Rectangle(0, 0, 220, HEADER_HEIGHT - 10) - self._prime_ad_widget = PrimeWidget() + self._prime_widget = PrimeWidget() + self._setup_widget = SetupWidget() self._exp_mode_button = ExperimentalModeButton() self._setup_callbacks() @@ -175,7 +177,7 @@ class HomeLayout(Widget): self.offroad_alert.render(self.content_rect) def _render_left_column(self): - self._prime_ad_widget.render(self.left_column_rect) + self._prime_widget.render(self.left_column_rect) def _render_right_column(self): exp_height = 125 @@ -190,8 +192,7 @@ class HomeLayout(Widget): self.right_column_rect.width, self.right_column_rect.height - exp_height - SPACING, ) - rl.draw_rectangle_rounded(setup_rect, 0.02, 10, PRIME_BG_COLOR) - gui_label(setup_rect, "Setup", 36, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + self._setup_widget.render(setup_rect) def _refresh(self): self.update_available = self.update_alert.refresh() diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py new file mode 100644 index 0000000000..a528d3c993 --- /dev/null +++ b/selfdrive/ui/widgets/setup.py @@ -0,0 +1,92 @@ +import pyray as rl +from openpilot.selfdrive.ui.lib.prime_state import PrimeType +from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.system.ui.lib.application import gui_app, Widget, FontWeight +from openpilot.system.ui.lib.button import gui_button, ButtonStyle +from openpilot.system.ui.lib.wrap_text import wrap_text + + +class SetupWidget(Widget): + def __init__(self): + super().__init__() + + self._pairing_dialog: PairingDialog | None = None + + def _render(self, rect: rl.Rectangle): + if ui_state.prime_state.get_type() == PrimeType.UNPAIRED: + self._render_registration(rect) + else: + self._render_firehose_prompt(rect) + + def _render_registration(self, rect: rl.Rectangle): + """Render registration prompt.""" + + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 590), 0.02, 20, rl.Color(51, 51, 51, 255)) + + x = rect.x + 64 + y = rect.y + 48 + w = rect.width - 128 + + # Title + font = gui_app.font(FontWeight.BOLD) + rl.draw_text_ex(font, "Finish Setup", rl.Vector2(x, y), 75, 0, rl.WHITE) + y += 113 # 75 + 38 spacing + + # Description + desc = "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer." + light_font = gui_app.font(FontWeight.LIGHT) + wrapped = wrap_text(light_font, desc, 50, int(w)) + for line in wrapped: + rl.draw_text_ex(light_font, line, rl.Vector2(x, y), 50, 0, rl.WHITE) + y += 50 + + button_rect = rl.Rectangle(x, y + 50, w, 128) + if gui_button(button_rect, "Pair device", button_style=ButtonStyle.PRIMARY): + self._show_pairing() + + def _render_firehose_prompt(self, rect: rl.Rectangle): + """Render firehose prompt widget.""" + + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 450), 0.02, 20, rl.Color(51, 51, 51, 255)) + + # Content margins (56, 40, 56, 40) + x = rect.x + 56 + y = rect.y + 40 + w = rect.width - 112 + spacing = 42 + + # Title with fire emojis + title_font = gui_app.font(FontWeight.MEDIUM) + title_text = "Firehose Mode" + rl.draw_text_ex(title_font, title_text, rl.Vector2(x, y), 64, 0, rl.WHITE) + y += 64 + spacing + + # Description + desc_font = gui_app.font(FontWeight.NORMAL) + desc_text = "Maximize your training data uploads to improve openpilot's driving models." + wrapped_desc = wrap_text(desc_font, desc_text, 40, int(w)) + + for line in wrapped_desc: + rl.draw_text_ex(desc_font, line, rl.Vector2(x, y), 40, 0, rl.WHITE) + y += 40 + + y += spacing + + # Open button + button_height = 48 + 64 # font size + padding + button_rect = rl.Rectangle(x, y, w, button_height) + if gui_button(button_rect, "Open", button_style=ButtonStyle.PRIMARY): + self._open_firehose_settings() + + def _show_pairing(self): + if not self._pairing_dialog: + self._pairing_dialog = PairingDialog() + gui_app.set_modal_overlay(self._pairing_dialog, lambda result: setattr(self, '_pairing_dialog', None)) + + def _open_firehose_settings(self): + pass + + def __del__(self): + if self._pairing_dialog: + del self._pairing_dialog