mici ui: consistent dialogs (#37671)

* new dialog

* clean up

* got wish

* use in mici reset

* punctuation

* clean up
This commit is contained in:
Shane Smiskol
2026-03-13 21:56:07 -07:00
committed by GitHub
parent 380d91c8f7
commit 46bbe6890a
6 changed files with 59 additions and 93 deletions
@@ -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)
+4 -4
View File
@@ -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
+37
View File
@@ -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):
+10 -36
View File
@@ -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):
+6 -14
View File
@@ -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):
+1 -38
View File
@@ -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