diff --git a/selfdrive/ui/mici/layouts/home.py b/selfdrive/ui/mici/layouts/home.py index af97de92f4e5de..cecad25dde6019 100644 --- a/selfdrive/ui/mici/layouts/home.py +++ b/selfdrive/ui/mici/layouts/home.py @@ -119,12 +119,6 @@ def show_event(self): def _update_params(self): self._experimental_mode = ui_state.params.get_bool("ExperimentalMode") - def _handle_long_press(self, _): - # long gating for experimental mode - only allow toggle if longitudinal control is available - if ui_state.has_longitudinal_control: - self._experimental_mode = not self._experimental_mode - ui_state.params.put("ExperimentalMode", self._experimental_mode) - def _update_state(self): if rl.get_time() - self._last_refresh > 5.0: device_state = ui_state.sm['deviceState'] @@ -143,6 +137,12 @@ def _update_network_status(self, device_state): def set_callbacks(self, on_settings: Callable | None = None): self._on_settings_click = on_settings + def _handle_long_press(self, _): + # long gating for experimental mode - only allow toggle if longitudinal control is available + if ui_state.has_longitudinal_control: + self._experimental_mode = not self._experimental_mode + ui_state.params.put("ExperimentalMode", self._experimental_mode) + def _handle_mouse_release(self, mouse_pos: MousePos): if self._on_settings_click: self._on_settings_click() diff --git a/selfdrive/ui/mici/layouts/settings/toggles.py b/selfdrive/ui/mici/layouts/settings/toggles.py index c16504fac8ba3a..ffcee9fb4907c7 100644 --- a/selfdrive/ui/mici/layouts/settings/toggles.py +++ b/selfdrive/ui/mici/layouts/settings/toggles.py @@ -6,6 +6,7 @@ from openpilot.selfdrive.ui.mici.widgets.button import BigParamControl, BigMultiParamToggle from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import NavWidget +from openpilot.selfdrive.ui.layouts.settings.toggles import DESCRIPTIONS from openpilot.selfdrive.ui.layouts.settings.common import restart_needed_callback from openpilot.selfdrive.ui.ui_state import ui_state @@ -17,14 +18,18 @@ def __init__(self, back_callback: Callable): super().__init__() self.set_back_callback(back_callback) - self._personality_toggle = BigMultiParamToggle("driving personality", "LongitudinalPersonality", ["aggressive", "standard", "relaxed"]) + self._personality_toggle = BigMultiParamToggle("driving personality", "LongitudinalPersonality", ["aggressive", "standard", "relaxed"], + description=DESCRIPTIONS["LongitudinalPersonality"]) self._experimental_btn = BigParamControl("experimental mode", "ExperimentalMode") - is_metric_toggle = BigParamControl("use metric units", "IsMetric") - ldw_toggle = BigParamControl("lane departure warnings", "IsLdwEnabled") - always_on_dm_toggle = BigParamControl("always-on driver monitor", "AlwaysOnDM") - record_front = BigParamControl("record & upload driver camera", "RecordFront", toggle_callback=restart_needed_callback) - record_mic = BigParamControl("record & upload mic audio", "RecordAudio", toggle_callback=restart_needed_callback) - enable_openpilot = BigParamControl("enable openpilot", "OpenpilotEnabledToggle", toggle_callback=restart_needed_callback) + is_metric_toggle = BigParamControl("use metric units", "IsMetric", description=DESCRIPTIONS["IsMetric"]) + ldw_toggle = BigParamControl("lane departure warnings", "IsLdwEnabled", description=DESCRIPTIONS["IsLdwEnabled"]) + always_on_dm_toggle = BigParamControl("always-on driver monitor", "AlwaysOnDM", description=DESCRIPTIONS["AlwaysOnDM"]) + record_front = BigParamControl("record & upload driver camera", "RecordFront", toggle_callback=restart_needed_callback, + description=DESCRIPTIONS["RecordFront"]) + record_mic = BigParamControl("record & upload mic audio", "RecordAudio", toggle_callback=restart_needed_callback, + description=DESCRIPTIONS["RecordAudio"]) + enable_openpilot = BigParamControl("enable openpilot", "OpenpilotEnabledToggle", toggle_callback=restart_needed_callback, + description=DESCRIPTIONS["OpenpilotEnabledToggle"]) self._scroller = Scroller([ self._personality_toggle, diff --git a/selfdrive/ui/mici/widgets/button.py b/selfdrive/ui/mici/widgets/button.py index be08e0fee3768f..a0bd4c7981e5cc 100644 --- a/selfdrive/ui/mici/widgets/button.py +++ b/selfdrive/ui/mici/widgets/button.py @@ -104,12 +104,13 @@ def _render(self, _): class BigButton(Widget): """A lightweight stand-in for the Qt BigButton, drawn & updated each frame.""" - def __init__(self, text: str, value: str = "", icon: Union[str, rl.Texture] = ""): + def __init__(self, text: str, value: str = "", icon: Union[str, rl.Texture] = "", description: str | None = None): super().__init__() self.set_rect(rl.Rectangle(0, 0, 402, 180)) self.text = text self.value = value self.set_icon(icon) + self.description = description self._scale_filter = BounceFilter(1.0, 0.1, 1 / gui_app.target_fps) @@ -136,6 +137,13 @@ def __init__(self, text: str, value: str = "", icon: Union[str, rl.Texture] = "" def set_icon(self, icon: Union[str, rl.Texture]): self._txt_icon = gui_app.texture(icon, 64, 64) if isinstance(icon, str) and len(icon) else icon + def _handle_long_press(self, mouse_pos: MousePos) -> None: + if not self.description: + return + # Import inside to avoid circular imports + from openpilot.selfdrive.ui.mici.widgets.dialog import BigDialog + gui_app.set_modal_overlay(BigDialog(self.text, self.description)) + def set_rotate_icon(self, rotate: bool): if rotate and self._rotate_icon_t is not None: return @@ -251,8 +259,9 @@ def _render(self, _): class BigToggle(BigButton): - def __init__(self, text: str, value: str = "", initial_state: bool = False, toggle_callback: Callable = None): - super().__init__(text, value, "") + def __init__(self, text: str, value: str = "", initial_state: bool = False, toggle_callback: Callable = None, + description: str | None = None): + super().__init__(text, value, "", description=description) self._checked = initial_state self._toggle_callback = toggle_callback @@ -289,8 +298,8 @@ def _render(self, _): class BigMultiToggle(BigToggle): def __init__(self, text: str, options: list[str], toggle_callback: Callable = None, - select_callback: Callable = None): - super().__init__(text, "", toggle_callback=toggle_callback) + select_callback: Callable = None, description: str | None = None): + super().__init__(text, "", toggle_callback=toggle_callback, description=description) assert len(options) > 0 self._options = options self._select_callback = select_callback @@ -328,8 +337,8 @@ def _render(self, _): class BigMultiParamToggle(BigMultiToggle): def __init__(self, text: str, param: str, options: list[str], toggle_callback: Callable = None, - select_callback: Callable = None): - super().__init__(text, options, toggle_callback, select_callback) + select_callback: Callable = None, description: str | None = None): + super().__init__(text, options, toggle_callback, select_callback, description=description) self._param = param self._params = Params() @@ -345,8 +354,8 @@ def _handle_mouse_release(self, mouse_pos: MousePos): class BigParamControl(BigToggle): - def __init__(self, text: str, param: str, toggle_callback: Callable = None): - super().__init__(text, "", toggle_callback=toggle_callback) + def __init__(self, text: str, param: str, toggle_callback: Callable = None, description: str | None = None): + super().__init__(text, "", toggle_callback=toggle_callback, description=description) self.param = param self.params = Params() self.set_checked(self.params.get_bool(self.param, False)) diff --git a/selfdrive/ui/mici/widgets/dialog.py b/selfdrive/ui/mici/widgets/dialog.py index 3d9aa3f9e247a3..785b58b02b3e6b 100644 --- a/selfdrive/ui/mici/widgets/dialog.py +++ b/selfdrive/ui/mici/widgets/dialog.py @@ -59,8 +59,12 @@ def __init__(self, right_btn: str | None = None, right_btn_callback: Callable | None = None): super().__init__(right_btn, right_btn_callback) - self._title = title - self._description = description + self._title_label = UnifiedLabel(title, font_size=50, text_color=rl.Color(255, 255, 255, int(255 * 0.9)), font_weight=FontWeight.BOLD) + self._description_label = UnifiedLabel(description, font_size=30, text_color=rl.Color(255, 255, 255, int(255 * 0.58)), ) + self._scroller = Scroller([ + self._title_label, + self._description_label + ], horizontal=False, snap_items=False) def _render(self, _) -> DialogResult: super()._render(_) @@ -73,26 +77,33 @@ def _render(self, _) -> DialogResult: if self._right_btn: max_width -= self._right_btn._rect.width - 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(int(self._rect.x + text_x_offset + PADDING), - int(self._rect.y + PADDING), - int(max_width), - int(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(int(self._rect.x + text_x_offset + PADDING), - int(self._rect.y + self._rect.height / 3), - int(max_width), - int(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._scroller.render(self._rect) + # title_height = self._title_label.get_text_height(int(max_width)) + # self._title_label.render(rl.Rectangle( + # self._rect.x, + # self._rect.y + # )) + + # 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(int(self._rect.x + text_x_offset + PADDING), + # int(self._rect.y + PADDING), + # int(max_width), + # int(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(int(self._rect.x + text_x_offset + PADDING), + # int(self._rect.y + self._rect.height / 3), + # int(max_width), + # int(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) return self._ret @@ -400,11 +411,10 @@ def _render(self, _): class BigDialogButton(BigButton): def __init__(self, text: str, value: str = "", icon: Union[str, rl.Texture] = "", description: str = ""): - super().__init__(text, value, icon) - self._description = description + super().__init__(text, value, icon, description=description) def _handle_mouse_release(self, mouse_pos: MousePos): super()._handle_mouse_release(mouse_pos) - dlg = BigDialog(self.text, self._description) + dlg = BigDialog(self.text, self.description) gui_app.set_modal_overlay(dlg)