Big UI: Quick Vehicle Panel Cleanup WIP

This commit is contained in:
firestarsdog
2026-07-14 17:47:31 -04:00
parent 6233e7b2c0
commit fc88f70ebd
@@ -7,9 +7,10 @@ from dataclasses import replace
import pyray as rl
from openpilot.system.hardware import HARDWARE
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.widgets import DialogResult
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets import DialogResult, Widget
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import _SettingsPage
@@ -28,7 +29,11 @@ from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
GROUP_HEADER_LINE_GAP,
draw_group_header,
draw_list_group_shell,
draw_rounded_fill,
draw_rounded_stroke,
draw_settings_list_row,
draw_soft_card,
mix_colors,
)
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
from openpilot.selfdrive.ui.lib.fingerprint_catalog import (
@@ -128,28 +133,41 @@ class VehicleSettingsManagerView(PanelManagerView):
def _build_steering_rows(self) -> list[SettingRow]:
cs = starpilot_state.car_state
rows = []
for key in ("DistanceButtonControl", "LongDistanceButtonControl", "VeryLongDistanceButtonControl"):
rows.append(SettingRow(key, "value", tr_noop(self._controller._action_title(key)),
get_value=lambda k=key: self._controller._get_action_name(k),
on_click=lambda k=key: self._controller._on_select(k)))
rows.append(SettingRow(
"combo:distance", "value", tr_noop("Distance Button"),
get_value=lambda: self._controller._get_action_name("DistanceButtonControl"),
on_click=lambda: self._controller._on_select("combo:distance"),
))
if cs.isBolt and cs.hasPedal and self._controller._params.get_bool("RemapCancelToDistance"):
for key in ("CancelButtonControl", "LongCancelButtonControl", "VeryLongCancelButtonControl"):
rows.append(SettingRow(key, "value", tr_noop(self._controller._action_title(key)),
get_value=lambda k=key: self._controller._get_action_name(k),
on_click=lambda k=key: self._controller._on_select(k)))
rows.append(SettingRow(
"combo:cancel", "value", tr_noop("Cancel Button"),
get_value=lambda: self._controller._get_action_name("CancelButtonControl"),
on_click=lambda: self._controller._on_select("combo:cancel"),
))
if not cs.isSubaru:
rows.append(SettingRow("LKASButtonControl", "value", tr_noop("LKAS Button"),
get_value=lambda: self._controller._get_action_name("LKASButtonControl"),
on_click=lambda: self._controller._on_select("LKASButtonControl")))
get_value=lambda: self._controller._get_action_name("LKASButtonControl"),
on_click=lambda: self._controller._on_select("LKASButtonControl")))
rows.append(SettingRow("MainCruiseButtonControl", "value", tr_noop("CC Main Button"),
get_value=lambda: self._controller._get_action_name("MainCruiseButtonControl"),
on_click=lambda: self._controller._on_select("MainCruiseButtonControl")))
if cs.hasModeStarButtons:
for key in ("ModeButtonControl", "LongModeButtonControl", "VeryLongModeButtonControl",
"StarButtonControl", "LongStarButtonControl", "VeryLongStarButtonControl"):
rows.append(SettingRow(key, "value", tr_noop(self._controller._action_title(key)),
get_value=lambda k=key: self._controller._get_action_name(k),
on_click=lambda k=key: self._controller._on_select(k)))
rows.append(SettingRow(
"combo:mode", "value", tr_noop("Mode Button"),
get_value=lambda: self._controller._get_action_name("ModeButtonControl"),
on_click=lambda: self._controller._on_select("combo:mode"),
))
rows.append(SettingRow(
"combo:star", "value", tr_noop("Star Button"),
get_value=lambda: self._controller._get_action_name("StarButtonControl"),
on_click=lambda: self._controller._on_select("combo:star"),
))
return rows
def _draw_row(self, rect: rl.Rectangle, row: SettingRow, is_last: bool):
@@ -158,13 +176,21 @@ class VehicleSettingsManagerView(PanelManagerView):
enabled = row.enabled() if row.enabled is not None else True
subtitle = row.disabled_label if not enabled and row.disabled_label else (tr(row.subtitle) if row.subtitle else "")
row_h = rect.height
if row_h < 100:
title_size = max(26, int(49 * row_h / 100))
subtitle = ""
else:
title_size = 49
if row.type == "value":
value_size = max(24, int(44 * row_h / 100)) if row_h < 100 else 44
value_text = row.get_value() if row.get_value else ""
draw_settings_list_row(rect, title=tr(row.title), subtitle=subtitle,
value=value_text, enabled=enabled,
hovered=hovered, pressed=pressed,
is_last=is_last, show_chevron=row.on_click is not None,
title_size=49, subtitle_size=38, value_size=44,
title_size=title_size, subtitle_size=38, value_size=value_size,
style=PANEL_STYLE)
elif row.type == "toggle":
toggle_value = row.get_state() if row.get_state else False
@@ -172,7 +198,7 @@ class VehicleSettingsManagerView(PanelManagerView):
toggle_value=toggle_value, enabled=enabled,
hovered=hovered, pressed=pressed,
is_last=is_last, show_chevron=False,
title_size=49, subtitle_size=38,
title_size=title_size, subtitle_size=38,
style=PANEL_STYLE)
def _draw_section(self, y: float, x: float, width: float, title: str, rows: list[SettingRow], row_height: float = ROW_HEIGHT) -> float:
@@ -416,6 +442,137 @@ class VehicleSettingsManagerView(PanelManagerView):
self._controller._on_select(value)
class ButtonActionComboDialog(Widget):
_DIALOG_W = 660
_DIALOG_H = 440
def __init__(self, controller: "StarPilotVehicleSettingsLayout", title: str,
keys: tuple[str, ...], labels: tuple[str, ...]):
super().__init__()
self._controller = controller
self._title = title
self._keys = keys
self._labels = labels
self._pressed_idx: int | None = None
self._pressed_close = False
self._font_title = gui_app.font(FontWeight.BOLD)
self._font_btn = gui_app.font(FontWeight.SEMI_BOLD)
def _layout(self):
sw = gui_app.width
sh = gui_app.height
dx = (sw - self._DIALOG_W) / 2
dy = (sh - self._DIALOG_H) / 2
d_rect = rl.Rectangle(int(dx), int(dy), int(self._DIALOG_W), int(self._DIALOG_H))
rows_area_y = dy + 72
rows_area_h = len(self._keys) * 88 + 12
rows_area = rl.Rectangle(int(dx + 40), int(rows_area_y), int(self._DIALOG_W - 80), int(rows_area_h))
row_rects = []
for i in range(len(self._keys)):
row_rects.append(rl.Rectangle(
int(dx + 40), int(rows_area_y + i * 88),
int(self._DIALOG_W - 80), 88,
))
close_w = 320
close_h = 64
close_rect = rl.Rectangle(
int(dx + (self._DIALOG_W - close_w) / 2),
int(dy + self._DIALOG_H - 36 - close_h),
close_w, close_h,
)
return d_rect, rows_area, row_rects, close_rect
def _handle_mouse_press(self, mouse_pos):
_, _, row_rects, close_rect = self._layout()
if rl.check_collision_point_rec(mouse_pos, close_rect):
self._pressed_close = True
return
for i, rect in enumerate(row_rects):
if rl.check_collision_point_rec(mouse_pos, rect):
self._pressed_idx = i
return
def _handle_mouse_release(self, mouse_pos):
if self._pressed_idx is not None:
idx = self._pressed_idx
self._pressed_idx = None
_, _, row_rects, _ = self._layout()
if idx < len(row_rects) and rl.check_collision_point_rec(mouse_pos, row_rects[idx]):
self._controller._show_action_picker(self._keys[idx])
return
if self._pressed_close:
self._pressed_close = False
_, _, _, close_rect = self._layout()
if rl.check_collision_point_rec(mouse_pos, close_rect):
gui_app.pop_widget()
def _render(self, rect):
sw = gui_app.width
sh = gui_app.height
rl.draw_rectangle(0, 0, sw, sh, rl.Color(0, 0, 0, 160))
d_rect, rows_area, row_rects, close_rect = self._layout()
mouse_pos = gui_app.last_mouse_event.pos
draw_rounded_fill(d_rect, rl.Color(10, 12, 16, 255), radius_px=24)
draw_rounded_stroke(d_rect, rl.Color(255, 255, 255, 16), radius_px=24)
rl.draw_rectangle_rec(rl.Rectangle(d_rect.x, d_rect.y, d_rect.width, 4), PANEL_STYLE.accent)
title_size = 32
ts = measure_text_cached(self._font_title, self._title, title_size)
rl.draw_text_ex(self._font_title, self._title,
rl.Vector2(int(d_rect.x + (d_rect.width - ts.x) / 2),
int(d_rect.y + (64 - title_size) / 2)),
title_size, 0, rl.WHITE)
draw_list_group_shell(rows_area, style=PANEL_STYLE)
for i in range(len(self._keys)):
row_rect = row_rects[i]
hovered = rl.check_collision_point_rec(mouse_pos, row_rect)
pressed = self._pressed_idx == i
draw_settings_list_row(
row_rect,
title=self._labels[i],
value=self._controller._get_action_name(self._keys[i]),
enabled=True,
hovered=hovered and (self._pressed_idx is None),
pressed=pressed,
is_last=(i == len(self._keys) - 1),
show_chevron=True,
title_size=46,
value_size=42,
style=PANEL_STYLE,
)
close_hovered = rl.check_collision_point_rec(mouse_pos, close_rect)
close_pressed = self._pressed_close
if close_pressed:
close_fill = mix_colors(PANEL_STYLE.accent, rl.Color(0, 0, 0, 255), 0.2)
close_border = PANEL_STYLE.accent
elif close_hovered:
close_fill = PANEL_STYLE.accent
close_border = PANEL_STYLE.accent
else:
close_fill = rl.Color(255, 255, 255, 14)
close_border = rl.Color(255, 255, 255, 28)
draw_soft_card(close_rect, close_fill, close_border)
close_text = tr("Close")
close_size = 22
cts = measure_text_cached(self._font_btn, close_text, close_size)
rl.draw_text_ex(self._font_btn, close_text,
rl.Vector2(int(close_rect.x + (close_rect.width - cts.x) / 2),
int(close_rect.y + (close_rect.height - cts.y) / 2)),
close_size, 0, rl.WHITE)
class StarPilotVehicleSettingsLayout(_SettingsPage):
def __init__(self):
super().__init__()
@@ -498,6 +655,9 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
gui_app.push_widget(ConfirmDialog(prompt, tr("Flash"), callback=on_confirm))
def _on_select(self, key: str):
if key.startswith("combo:"):
self._show_button_combo_dialog(key)
return
if key in ("CarMake", "CarModel") and not self._params.get_bool("ForceFingerprint"):
return
if key == "CarMake":
@@ -511,6 +671,37 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
else:
self._show_action_picker(key)
def _show_button_combo_dialog(self, key: str):
combo_configs = {
"combo:distance": {
"title": tr_noop("Distance Button"),
"keys": ("DistanceButtonControl", "LongDistanceButtonControl", "VeryLongDistanceButtonControl"),
"labels": (tr_noop("Press"), tr_noop("Long Press"), tr_noop("Very Long")),
},
"combo:cancel": {
"title": tr_noop("Cancel Button"),
"keys": ("CancelButtonControl", "LongCancelButtonControl", "VeryLongCancelButtonControl"),
"labels": (tr_noop("Press"), tr_noop("Long Press"), tr_noop("Very Long")),
},
"combo:mode": {
"title": tr_noop("Mode Button"),
"keys": ("ModeButtonControl", "LongModeButtonControl", "VeryLongModeButtonControl"),
"labels": (tr_noop("Press"), tr_noop("Long Press"), tr_noop("Very Long")),
},
"combo:star": {
"title": tr_noop("Star Button"),
"keys": ("StarButtonControl", "LongStarButtonControl", "VeryLongStarButtonControl"),
"labels": (tr_noop("Press"), tr_noop("Long Press"), tr_noop("Very Long")),
},
}
config = combo_configs.get(key)
if config is None:
return
gui_app.push_widget(ButtonActionComboDialog(
self, tr(config["title"]), config["keys"],
tuple(tr(l) for l in config["labels"]),
))
def _on_select_make(self):
makes = list(self._make_options)
if not makes: