From 46bbe6890a8888e3e7db95b931650f024a64c0fb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 13 Mar 2026 21:56:07 -0700 Subject: [PATCH] mici ui: consistent dialogs (#37671) * new dialog * clean up * got wish * use in mici reset * punctuation * clean up --- .../ui/mici/layouts/settings/developer.py | 2 +- selfdrive/ui/mici/layouts/settings/device.py | 8 ++-- selfdrive/ui/mici/widgets/button.py | 37 +++++++++++++++ selfdrive/ui/mici/widgets/dialog.py | 46 ++++--------------- system/ui/mici_reset.py | 20 +++----- system/ui/mici_setup.py | 39 +--------------- 6 files changed, 59 insertions(+), 93 deletions(-) diff --git a/selfdrive/ui/mici/layouts/settings/developer.py b/selfdrive/ui/mici/layouts/settings/developer.py index b471c50f9..386b46892 100644 --- a/selfdrive/ui/mici/layouts/settings/developer.py +++ b/selfdrive/ui/mici/layouts/settings/developer.py @@ -35,7 +35,7 @@ class DeveloperLayoutMici(NavScroller): github_username = ui_state.params.get("GithubUsername") or "" dlg = BigInputDialog("enter GitHub username...", github_username, minimum_length=0, confirm_callback=github_username_callback) if not system_time_valid(): - dlg = BigDialog("Please connect to Wi-Fi to fetch your key", "") + dlg = BigDialog("", "Please connect to Wi-Fi to fetch your key.") gui_app.push_widget(dlg) return gui_app.push_widget(dlg) diff --git a/selfdrive/ui/mici/layouts/settings/device.py b/selfdrive/ui/mici/layouts/settings/device.py index 909e30ac7..3c165b5bb 100644 --- a/selfdrive/ui/mici/layouts/settings/device.py +++ b/selfdrive/ui/mici/layouts/settings/device.py @@ -74,7 +74,7 @@ def _engaged_confirmation_click(callback: Callable, action_text: str, icon: rl.T gui_app.push_widget(BigConfirmationDialog(f"slide to\n{action_text.lower()}", icon, confirm_callback, exit_on_confirm=exit_on_confirm, red=red)) else: - gui_app.push_widget(BigDialog(f"Disengage to {action_text}", "")) + gui_app.push_widget(BigDialog("", f"Disengage to {action_text}")) class EngagedConfirmationCircleButton(BigCircleButton): @@ -156,9 +156,9 @@ class PairBigButton(BigButton): return dlg: BigDialog | PairingDialog if not system_time_valid(): - dlg = BigDialog(tr("Please connect to Wi-Fi to complete initial pairing"), "") + dlg = BigDialog("", tr("Please connect to Wi-Fi to complete initial pairing.")) elif UNREGISTERED_DONGLE_ID == (ui_state.params.get("DongleId") or UNREGISTERED_DONGLE_ID): - dlg = BigDialog(tr("Device must be registered with the comma.ai backend to pair"), "") + dlg = BigDialog("", tr("Device must be registered with the comma.ai backend to pair.")) else: dlg = PairingDialog() gui_app.push_widget(dlg) @@ -188,7 +188,7 @@ class UpdateOpenpilotBigButton(BigButton): super()._handle_mouse_release(mouse_pos) if not system_time_valid(): - dlg = BigDialog(tr("Please connect to Wi-Fi to update"), "") + dlg = BigDialog("", tr("Please connect to Wi-Fi to update.")) gui_app.push_widget(dlg) return diff --git a/selfdrive/ui/mici/widgets/button.py b/selfdrive/ui/mici/widgets/button.py index 9724a1819..058c351fb 100644 --- a/selfdrive/ui/mici/widgets/button.py +++ b/selfdrive/ui/mici/widgets/button.py @@ -335,6 +335,43 @@ class BigMultiToggle(BigToggle): y += 35 +class GreyBigButton(BigButton): + """Users should manage newlines with this class themselves""" + + LABEL_HORIZONTAL_PADDING = 30 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_touch_valid_callback(lambda: False) + + self._rect.width = 476 + + self._label.set_font_size(36) + self._label.set_font_weight(FontWeight.BOLD) + self._label.set_line_height(1.0) + + self._sub_label.set_font_size(36) + self._sub_label.set_text_color(rl.Color(255, 255, 255, int(255 * 0.9))) + self._sub_label.set_font_weight(FontWeight.DISPLAY_REGULAR) + self._sub_label.set_alignment_vertical(rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE if not self._label.text else + rl.GuiTextAlignmentVertical.TEXT_ALIGN_BOTTOM) + self._sub_label.set_line_height(0.95) + + @property + def LABEL_VERTICAL_PADDING(self): + return BigButton.LABEL_VERTICAL_PADDING if self._label.text else 18 + + def _width_hint(self) -> int: + return int(self._rect.width - self.LABEL_HORIZONTAL_PADDING * 2) + + def _get_label_font_size(self): + return 36 + + def _render(self, _): + rl.draw_rectangle_rounded(self._rect, 0.4, 10, rl.Color(255, 255, 255, int(255 * 0.15))) + self._draw_content(self._rect.y) + + class BigMultiParamToggle(BigMultiToggle): def __init__(self, text: str, param: str, options: list[str], toggle_callback: Callable | None = None, select_callback: Callable | None = None): diff --git a/selfdrive/ui/mici/widgets/dialog.py b/selfdrive/ui/mici/widgets/dialog.py index 396ea35cc..ed1466449 100644 --- a/selfdrive/ui/mici/widgets/dialog.py +++ b/selfdrive/ui/mici/widgets/dialog.py @@ -4,14 +4,13 @@ import pyray as rl from typing import Union from collections.abc import Callable from openpilot.system.ui.widgets.nav_widget import NavWidget -from openpilot.system.ui.widgets.label import UnifiedLabel, gui_label +from openpilot.system.ui.widgets.label import UnifiedLabel from openpilot.system.ui.widgets.mici_keyboard import MiciKeyboard from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.widgets.slider import RedBigSlider, BigSlider from openpilot.common.filter_simple import FirstOrderFilter -from openpilot.selfdrive.ui.mici.widgets.button import BigCircleButton, BigButton +from openpilot.selfdrive.ui.mici.widgets.button import BigCircleButton, BigButton, GreyBigButton DEBUG = False @@ -25,42 +24,17 @@ class BigDialogBase(NavWidget, abc.ABC): class BigDialog(BigDialogBase): - def __init__(self, - title: str, - description: str): + def __init__(self, title: str, description: str, icon: Union[rl.Texture, None] = None): super().__init__() - self._title = title - self._description = description + self._card = GreyBigButton(title, description, icon) def _render(self, _): - super()._render(_) - - # draw title - # TODO: we desperately need layouts - # TODO: coming up with these numbers manually is a pain and not scalable - # TODO: no clue what any of these numbers mean. VBox and HBox would remove all of this shite - max_width = self._rect.width - PADDING * 2 - - title_wrapped = '\n'.join(wrap_text(gui_app.font(FontWeight.BOLD), self._title, 50, int(max_width))) - title_size = measure_text_cached(gui_app.font(FontWeight.BOLD), title_wrapped, 50) - text_x_offset = 0 - title_rect = rl.Rectangle(self._rect.x + text_x_offset + PADDING, - self._rect.y + PADDING, - max_width, - title_size.y) - gui_label(title_rect, title_wrapped, 50, font_weight=FontWeight.BOLD, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) - - # draw description - desc_wrapped = '\n'.join(wrap_text(gui_app.font(FontWeight.MEDIUM), self._description, 30, int(max_width))) - desc_size = measure_text_cached(gui_app.font(FontWeight.MEDIUM), desc_wrapped, 30) - desc_rect = rl.Rectangle(self._rect.x + text_x_offset + PADDING, - self._rect.y + self._rect.height / 3, - max_width, - desc_size.y) - # TODO: text align doesn't seem to work properly with newlines - gui_label(desc_rect, desc_wrapped, 30, font_weight=FontWeight.MEDIUM, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + self._card.render(rl.Rectangle( + self._rect.x + self._rect.width / 2 - self._card.rect.width / 2, + self._rect.y + self._rect.height / 2 - self._card.rect.height / 2, + self._card.rect.width, + self._card.rect.height, + )) class BigConfirmationDialog(BigDialogBase): diff --git a/system/ui/mici_reset.py b/system/ui/mici_reset.py index af9d866d5..9cc6e7f3f 100755 --- a/system/ui/mici_reset.py +++ b/system/ui/mici_reset.py @@ -10,9 +10,8 @@ import pyray as rl from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets.scroller import Scroller -from openpilot.system.ui.widgets.nav_widget import NavWidget from openpilot.system.ui.mici_setup import GreyBigButton, FailedPage -from openpilot.selfdrive.ui.mici.widgets.dialog import BigConfirmationCircleButton +from openpilot.selfdrive.ui.mici.widgets.dialog import BigDialog, BigConfirmationCircleButton USERDATA = "/dev/disk/by-partlabel/userdata" TIMEOUT = 3*60 @@ -36,16 +35,14 @@ class ResetFailedPage(FailedPage): return False -class ResettingPage(NavWidget): +class ResettingPage(BigDialog): DOT_STEP = 0.6 def __init__(self): - super().__init__() + super().__init__("resetting device", "this may take up to\na minute...", + gui_app.texture("icons_mici/setup/factory_reset.png", 64, 64)) self._show_time = 0.0 - self._resetting_card = GreyBigButton("resetting device", "this may take up to\na minute...", - gui_app.texture("icons_mici/setup/factory_reset.png", 64, 64)) - def show_event(self): super().show_event() self._nav_bar._alpha = 0.0 # not dismissable @@ -57,13 +54,8 @@ class ResettingPage(NavWidget): def _render(self, _): t = (rl.get_time() - self._show_time) % (self.DOT_STEP * 2) dots = "." * min(int(t / (self.DOT_STEP / 4)), 3) - self._resetting_card.set_value(f"this may take up to\na minute{dots}") - self._resetting_card.render(rl.Rectangle( - self._rect.x + self._rect.width / 2 - self._resetting_card.rect.width / 2, - self._rect.y + self._rect.height / 2 - self._resetting_card.rect.height / 2, - self._resetting_card.rect.width, - self._resetting_card.rect.height, - )) + self._card.set_value(f"this may take up to\na minute{dots}") + super()._render(_) class Reset(Scroller): diff --git a/system/ui/mici_setup.py b/system/ui/mici_setup.py index 62d9dcd82..d55fc5e1e 100755 --- a/system/ui/mici_setup.py +++ b/system/ui/mici_setup.py @@ -28,7 +28,7 @@ from openpilot.system.ui.widgets.slider import LargerSlider from openpilot.selfdrive.ui.mici.layouts.settings.network import WifiNetworkButton from openpilot.selfdrive.ui.mici.layouts.settings.network.wifi_ui import WifiUIMici from openpilot.selfdrive.ui.mici.widgets.dialog import BigInputDialog, BigConfirmationCircleButton -from openpilot.selfdrive.ui.mici.widgets.button import BigButton +from openpilot.selfdrive.ui.mici.widgets.button import BigButton, GreyBigButton NetworkType = log.DeviceState.NetworkType @@ -254,43 +254,6 @@ class FailedPage(NavScroller): self._reason_card.set_visible(False) -class GreyBigButton(BigButton): - """Users should manage newlines with this class themselves""" - - LABEL_HORIZONTAL_PADDING = 30 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_touch_valid_callback(lambda: False) - - self._rect.width = 476 - - self._label.set_font_size(36) - self._label.set_font_weight(FontWeight.BOLD) - self._label.set_line_height(1.0) - - self._sub_label.set_font_size(36) - self._sub_label.set_text_color(rl.Color(255, 255, 255, int(255 * 0.9))) - self._sub_label.set_font_weight(FontWeight.DISPLAY_REGULAR) - self._sub_label.set_alignment_vertical(rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE if not self._label.text else - rl.GuiTextAlignmentVertical.TEXT_ALIGN_BOTTOM) - self._sub_label.set_line_height(0.95) - - @property - def LABEL_VERTICAL_PADDING(self): - return BigButton.LABEL_VERTICAL_PADDING if self._label.text else 18 - - def _width_hint(self) -> int: - return int(self._rect.width - self.LABEL_HORIZONTAL_PADDING * 2) - - def _get_label_font_size(self): - return 36 - - def _render(self, _): - rl.draw_rectangle_rounded(self._rect, 0.4, 10, rl.Color(255, 255, 255, int(255 * 0.15))) - self._draw_content(self._rect.y) - - class BigPillButton(BigButton): def __init__(self, *args, green: bool = False, disabled_background: bool = False, **kwargs): self._green = green