mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-13 21:32:14 +08:00
BigUI WIP: Vehicle
This commit is contained in:
@@ -2,35 +2,30 @@ from __future__ import annotations
|
||||
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import replace
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.ui.lib.application import FontWeight, gui_app
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult, Widget
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
|
||||
from openpilot.system.ui.widgets.label import gui_label
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import _SettingsPage
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
|
||||
AetherListMetrics,
|
||||
AetherListColors,
|
||||
AetherSliderDialog,
|
||||
COMPACT_PANEL_METRICS,
|
||||
DEFAULT_PANEL_STYLE,
|
||||
PanelManagerView,
|
||||
SettingRow,
|
||||
TileGrid,
|
||||
GROUP_HEADER_HEIGHT,
|
||||
GROUP_HEADER_GAP,
|
||||
draw_group_header,
|
||||
draw_list_group_shell,
|
||||
draw_section_header,
|
||||
draw_selection_list_row,
|
||||
draw_settings_list_row,
|
||||
draw_settings_panel_header,
|
||||
draw_soft_card,
|
||||
TileGrid,
|
||||
ToggleTile,
|
||||
with_alpha,
|
||||
draw_status_badges,
|
||||
wrap_text,
|
||||
)
|
||||
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
|
||||
from openpilot.selfdrive.ui.lib.fingerprint_catalog import (
|
||||
@@ -70,62 +65,245 @@ def _lock_doors_timer_labels():
|
||||
return labels
|
||||
|
||||
|
||||
CUSTOM_METRICS = AetherListMetrics(
|
||||
max_content_width=1560,
|
||||
outer_margin_x=18,
|
||||
outer_margin_y=10,
|
||||
panel_padding_x=16,
|
||||
panel_padding_top=16,
|
||||
panel_padding_bottom=12,
|
||||
header_height=0,
|
||||
section_gap=12,
|
||||
section_header_height=28,
|
||||
section_header_gap=8,
|
||||
row_height=104,
|
||||
utility_row_height=88,
|
||||
)
|
||||
|
||||
SECTION_GAP = CUSTOM_METRICS.section_gap
|
||||
SECTION_HEADER_HEIGHT = CUSTOM_METRICS.section_header_height
|
||||
SECTION_HEADER_GAP = CUSTOM_METRICS.section_header_gap
|
||||
ROW_HEIGHT = CUSTOM_METRICS.row_height
|
||||
FADE_HEIGHT = CUSTOM_METRICS.fade_height
|
||||
SECTION_GAP = 16
|
||||
SECTION_HEADER_HEIGHT = 30
|
||||
SECTION_HEADER_GAP = 8
|
||||
GROUP_OVERHEAD = GROUP_HEADER_HEIGHT + GROUP_HEADER_GAP
|
||||
ROW_HEIGHT = 86.0
|
||||
PANEL_STYLE = DEFAULT_PANEL_STYLE
|
||||
|
||||
|
||||
class VehicleSettingsManagerView(PanelManagerView):
|
||||
HEADER_SUBTITLE_HEIGHT = 22
|
||||
HEADER_SUMMARY_GAP = 10
|
||||
HEADER_CARD_HEIGHT = 100
|
||||
METRICS = CUSTOM_METRICS
|
||||
METRICS = replace(COMPACT_PANEL_METRICS,
|
||||
header_height=0,
|
||||
outer_margin_y=14,
|
||||
panel_padding_top=16,
|
||||
panel_padding_bottom=14,
|
||||
section_header_height=30,
|
||||
section_header_gap=8,
|
||||
)
|
||||
|
||||
def __init__(self, controller: "StarPilotVehicleSettingsLayout"):
|
||||
super().__init__()
|
||||
self._controller = controller
|
||||
self._shell_rect = rl.Rectangle(0, 0, 0, 0)
|
||||
|
||||
self._toggle_grid = TileGrid(columns=2, padding=12, force_square=True, min_tile_width=100, min_tile_height=130.0, max_tile_height=280.0)
|
||||
self._toggle_grid = TileGrid(columns=2, padding=12, force_square=True,
|
||||
min_tile_height=130.0, max_tile_height=280.0)
|
||||
self.register_page_grid(self._toggle_grid)
|
||||
|
||||
self._last_make = ""
|
||||
self._last_model = ""
|
||||
self._left_row_height = ROW_HEIGHT
|
||||
|
||||
@property
|
||||
def vertical_scrolling_disabled(self) -> bool:
|
||||
return True
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
starpilot_state.update(force=True)
|
||||
self._rebuild_toggle_grid()
|
||||
|
||||
def _build_header_chips(self) -> list[str]:
|
||||
cs = starpilot_state.car_state
|
||||
chips = []
|
||||
if cs.canUsePedal: chips.append(tr("Pedal"))
|
||||
if cs.hasSASCM: chips.append(tr("SASCM"))
|
||||
if cs.canUseSDSU: chips.append(tr("SDSU"))
|
||||
if cs.hasZSS: chips.append(tr("ZSS"))
|
||||
if cs.hasRadar: chips.append(tr("Radar"))
|
||||
if cs.hasOpenpilotLongitudinal: chips.append(tr("Long"))
|
||||
if cs.hasBSM: chips.append(tr("BSM"))
|
||||
if cs.hasSNG: chips.append(tr("SNG"))
|
||||
return chips
|
||||
|
||||
def _build_identity_rows(self) -> list[SettingRow]:
|
||||
cs = starpilot_state.car_state
|
||||
fp = lambda: self._controller._params.get_bool("ForceFingerprint")
|
||||
dl = "Enable Disable Fingerprinting to change."
|
||||
rows = [
|
||||
SettingRow("ForceFingerprint", "toggle", tr_noop("Disable Fingerprinting"),
|
||||
subtitle=tr_noop("Manually select vehicle instead of auto-detecting."),
|
||||
get_state=fp,
|
||||
set_state=lambda s: self._controller._on_toggle("ForceFingerprint")),
|
||||
SettingRow("CarMake", "value", tr_noop("Car Make"),
|
||||
get_value=self._controller._get_display_make,
|
||||
on_click=lambda: self._controller._on_select("CarMake"),
|
||||
enabled=fp, disabled_label=dl),
|
||||
SettingRow("CarModel", "value", tr_noop("Car Model"),
|
||||
get_value=self._controller._get_display_model,
|
||||
on_click=lambda: self._controller._on_select("CarModel"),
|
||||
enabled=fp, disabled_label=dl),
|
||||
]
|
||||
if cs.isToyota:
|
||||
rows.append(SettingRow("LockDoorsTimer", "value", tr_noop("Lock Doors Timer"),
|
||||
get_value=lambda: _lock_doors_timer_labels().get(
|
||||
float(self._controller._params.get_int("LockDoorsTimer")),
|
||||
f"{self._controller._params.get_int('LockDoorsTimer')}s"),
|
||||
on_click=lambda: self._controller._on_select("LockDoorsTimer")))
|
||||
rows.append(SettingRow("ClusterOffset", "value", tr_noop("Dashboard Speed Offset"),
|
||||
get_value=lambda: f"{self._controller._params.get_float('ClusterOffset'):.3f}x",
|
||||
on_click=lambda: self._controller._on_select("ClusterOffset")))
|
||||
return rows
|
||||
|
||||
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)))
|
||||
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)))
|
||||
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")))
|
||||
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)))
|
||||
return rows
|
||||
|
||||
def _draw_row(self, rect: rl.Rectangle, row: SettingRow, is_last: bool):
|
||||
target_id = f"{row.type}:{row.id}"
|
||||
hovered, pressed = self._interactive_state(target_id, rect)
|
||||
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 "")
|
||||
|
||||
if row.type == "value":
|
||||
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=34, subtitle_size=22, value_size=28,
|
||||
style=PANEL_STYLE)
|
||||
elif row.type == "toggle":
|
||||
toggle_value = row.get_state() if row.get_state else False
|
||||
draw_settings_list_row(rect, title=tr(row.title), subtitle=subtitle,
|
||||
toggle_value=toggle_value, enabled=enabled,
|
||||
hovered=hovered, pressed=pressed,
|
||||
is_last=is_last, show_chevron=False,
|
||||
title_size=34, subtitle_size=22,
|
||||
style=PANEL_STYLE)
|
||||
|
||||
def _draw_section(self, y: float, x: float, width: float, title: str, rows: list[SettingRow], row_height: float = ROW_HEIGHT) -> float:
|
||||
if title:
|
||||
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), tr(title), style=PANEL_STYLE)
|
||||
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
group_h = len(rows) * row_height
|
||||
draw_list_group_shell(rl.Rectangle(x, y, width, group_h), style=PANEL_STYLE)
|
||||
for i, row in enumerate(rows):
|
||||
self._draw_row(rl.Rectangle(x, y + i * row_height, width, row_height), row, i == len(rows) - 1)
|
||||
return y + group_h
|
||||
|
||||
def _draw_scroll_content(self, rect: rl.Rectangle, width: float):
|
||||
y = rect.y + self._scroll_offset
|
||||
identity_rows = self._build_identity_rows()
|
||||
steering_rows = self._build_steering_rows()
|
||||
|
||||
if self._uses_two_columns(width):
|
||||
col_w = self._column_width(width)
|
||||
rx = rect.x + col_w + self.COLUMN_GAP
|
||||
|
||||
draw_section_header(rl.Rectangle(rect.x, y, col_w, SECTION_HEADER_HEIGHT),
|
||||
tr("Vehicle Identity"), style=PANEL_STYLE)
|
||||
|
||||
caps = self._build_header_chips()
|
||||
trailing = " " + tr("Vehicle Capabilities") + " " + " ".join(caps) if caps else ""
|
||||
draw_section_header(rl.Rectangle(rx, y, col_w, SECTION_HEADER_HEIGHT),
|
||||
tr("Features"), trailing_text=trailing, style=PANEL_STYLE)
|
||||
|
||||
content_y = y + SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
|
||||
draw_list_group_shell(rl.Rectangle(rect.x, content_y, col_w, self._container_h), style=PANEL_STYLE)
|
||||
row_y = content_y
|
||||
for i, row in enumerate(identity_rows):
|
||||
self._draw_row(rl.Rectangle(rect.x, row_y, col_w, self._left_row_height),
|
||||
row, i == len(identity_rows) - 1 and not steering_rows)
|
||||
row_y += self._left_row_height
|
||||
|
||||
if steering_rows:
|
||||
row_y = draw_group_header(rect.x + 24, row_y, col_w - 48, tr("STEERING CONTROLS"))
|
||||
for i, row in enumerate(steering_rows):
|
||||
self._draw_row(rl.Rectangle(rect.x, row_y, col_w, self._left_row_height),
|
||||
row, i == len(steering_rows) - 1)
|
||||
row_y += self._left_row_height
|
||||
|
||||
if self._toggle_grid.tiles:
|
||||
self._draw_two_column_tile_grid(self._toggle_grid, rx, content_y, col_w,
|
||||
self._container_h, title=None, style=PANEL_STYLE)
|
||||
else:
|
||||
y = self._draw_section(y, rect.x, width, tr("Vehicle Identity"), identity_rows, self._left_row_height)
|
||||
y += SECTION_GAP
|
||||
y = self._draw_section(y, rect.x, width, tr("Steering Controls"), steering_rows, self._left_row_height)
|
||||
y += SECTION_GAP
|
||||
|
||||
if self._toggle_grid.tiles:
|
||||
draw_section_header(rl.Rectangle(rect.x, y, width, SECTION_HEADER_HEIGHT),
|
||||
tr("Features"), style=PANEL_STYLE)
|
||||
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
self._toggle_grid._columns = 3
|
||||
avail = width - 24
|
||||
th = self.measure_page_grid_height(self._toggle_grid, avail)
|
||||
draw_list_group_shell(rl.Rectangle(rect.x, y, width, th + 24), style=PANEL_STYLE)
|
||||
self._render_page_grid(self._toggle_grid, rl.Rectangle(rect.x + 12, y + 12, avail, th))
|
||||
|
||||
def _measure_content_height(self, width: float) -> float:
|
||||
self._check_rebuild_grid()
|
||||
identity_rows = self._build_identity_rows()
|
||||
steering_rows = self._build_steering_rows()
|
||||
total_rows = len(identity_rows) + len(steering_rows)
|
||||
|
||||
tiles_h = 0.0
|
||||
if self._toggle_grid.tiles:
|
||||
if self._uses_two_columns(width):
|
||||
self._toggle_grid._columns = 2
|
||||
col_w = self._column_width(width)
|
||||
tiles_h = self.measure_page_grid_height(self._toggle_grid, col_w - 24)
|
||||
else:
|
||||
self._toggle_grid._columns = 3
|
||||
tiles_h = self.measure_page_grid_height(self._toggle_grid, width - 24)
|
||||
|
||||
if self._uses_two_columns(width):
|
||||
left_content_natural = total_rows * ROW_HEIGHT + GROUP_OVERHEAD
|
||||
header_h = SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
|
||||
if self._scroll_rect:
|
||||
available_content_h = self._scroll_rect.height - header_h
|
||||
else:
|
||||
available_content_h = max(left_content_natural, tiles_h)
|
||||
|
||||
if left_content_natural > available_content_h and total_rows > 0:
|
||||
self._left_row_height = max(48.0, available_content_h / total_rows)
|
||||
else:
|
||||
self._left_row_height = ROW_HEIGHT
|
||||
|
||||
left_content_h = total_rows * self._left_row_height + GROUP_OVERHEAD
|
||||
self._container_h = max(left_content_h, tiles_h)
|
||||
|
||||
total_h = self._compute_two_column_height(header_h + self._container_h)
|
||||
self._container_h = total_h - header_h
|
||||
return total_h
|
||||
|
||||
self._left_row_height = ROW_HEIGHT
|
||||
identity_natural_h = self._section_block_height(self._section_height(len(identity_rows), ROW_HEIGHT))
|
||||
steering_natural_h = self._section_block_height(self._section_height(len(steering_rows), ROW_HEIGHT))
|
||||
left_natural_h = identity_natural_h + SECTION_GAP + steering_natural_h
|
||||
return left_natural_h + tiles_h + (SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP if tiles_h else 0)
|
||||
|
||||
def _build_driving_toggles(self) -> list[dict]:
|
||||
cs = starpilot_state.car_state
|
||||
toggles = []
|
||||
|
||||
toggles.append({
|
||||
"title": tr("Disable Fingerprinting"),
|
||||
"subtitle": tr("Manually select vehicle instead of auto-detecting."),
|
||||
"get_state": lambda: self._controller._params.get_bool("ForceFingerprint"),
|
||||
"set_state": lambda s: self._controller._on_toggle("ForceFingerprint"),
|
||||
})
|
||||
|
||||
toggles.append({
|
||||
"title": tr("Disable openpilot Long"),
|
||||
"subtitle": tr("Revert to stock longitudinal control."),
|
||||
@@ -242,7 +420,7 @@ class VehicleSettingsManagerView(PanelManagerView):
|
||||
self._last_model = current_model
|
||||
self._rebuild_toggle_grid()
|
||||
|
||||
def _on_frame_created(self, frame) -> None:
|
||||
def _on_frame_created(self, frame):
|
||||
self._shell_rect = frame.shell
|
||||
|
||||
def _activate_target(self, target_id: str | None):
|
||||
@@ -251,269 +429,9 @@ class VehicleSettingsManagerView(PanelManagerView):
|
||||
prefix, _, value = target_id.partition(":")
|
||||
if prefix == "toggle":
|
||||
self._controller._on_toggle(value)
|
||||
elif prefix == "select":
|
||||
elif prefix == "value":
|
||||
self._controller._on_select(value)
|
||||
|
||||
def _draw_header(self, rect: rl.Rectangle):
|
||||
pass
|
||||
|
||||
def _draw_summary_card(self, rect: rl.Rectangle):
|
||||
draw_soft_card(rect, PANEL_STYLE.surface_fill, PANEL_STYLE.surface_border)
|
||||
|
||||
inset = 24
|
||||
avail_w = rect.width - inset * 2
|
||||
col1_w = avail_w * 0.32
|
||||
col2_w = avail_w * 0.20
|
||||
col3_w = avail_w * 0.24
|
||||
col4_w = avail_w * 0.24
|
||||
|
||||
col1_x = rect.x + inset
|
||||
col2_x = col1_x + col1_w
|
||||
col3_x = col2_x + col2_w
|
||||
col4_x = col3_x + col3_w
|
||||
|
||||
# 1. Current Vehicle
|
||||
make = self._controller._get_display_make()
|
||||
model = self._controller._get_display_model()
|
||||
vehicle_name = f"{make} {model}" if make != tr("None") else tr("No vehicle selected")
|
||||
|
||||
gui_label(rl.Rectangle(col1_x, rect.y + 16, col1_w - 16, 18), tr("CURRENT VEHICLE"), 14, AetherListColors.MUTED, FontWeight.MEDIUM)
|
||||
gui_label(rl.Rectangle(col1_x, rect.y + 38, col1_w - 16, 28), vehicle_name, 22, AetherListColors.HEADER, FontWeight.BOLD)
|
||||
|
||||
# 2. Fingerprint
|
||||
fingerprint_state = tr("Forced") if self._controller._params.get_bool("ForceFingerprint") else tr("Auto")
|
||||
gui_label(rl.Rectangle(col2_x, rect.y + 16, col2_w - 16, 18), tr("FINGERPRINT"), 14, AetherListColors.MUTED, FontWeight.MEDIUM)
|
||||
gui_label(rl.Rectangle(col2_x, rect.y + 38, col2_w - 16, 26), fingerprint_state, 20, AetherListColors.HEADER, FontWeight.SEMI_BOLD)
|
||||
|
||||
# 3. Hardware
|
||||
cs = starpilot_state.car_state
|
||||
hardware_items = []
|
||||
if cs.canUsePedal:
|
||||
hardware_items.append(tr("Pedal"))
|
||||
if cs.hasSASCM:
|
||||
hardware_items.append(tr("SASCM"))
|
||||
if cs.canUseSDSU:
|
||||
hardware_items.append(tr("SDSU"))
|
||||
if cs.hasZSS:
|
||||
hardware_items.append(tr("ZSS"))
|
||||
|
||||
gui_label(rl.Rectangle(col3_x, rect.y + 16, col3_w - 16, 18), tr("HARDWARE"), 14, AetherListColors.MUTED, FontWeight.MEDIUM)
|
||||
if not hardware_items:
|
||||
gui_label(rl.Rectangle(col3_x, rect.y + 38, col3_w - 16, 26), tr("Standard"), 18, AetherListColors.MUTED, FontWeight.MEDIUM)
|
||||
else:
|
||||
draw_status_badges(col3_x, rect.y + 37, hardware_items, PANEL_STYLE)
|
||||
|
||||
# 4. Capabilities
|
||||
gui_label(rl.Rectangle(col4_x, rect.y + 16, col4_w - 16, 18), tr("CAPABILITIES"), 14, AetherListColors.MUTED, FontWeight.MEDIUM)
|
||||
|
||||
metrics = []
|
||||
if cs.hasRadar:
|
||||
metrics.append(tr("Radar"))
|
||||
if cs.hasOpenpilotLongitudinal:
|
||||
metrics.append(tr("Long"))
|
||||
if cs.hasBSM:
|
||||
metrics.append(tr("BSM"))
|
||||
if cs.hasSNG:
|
||||
metrics.append(tr("SNG"))
|
||||
|
||||
if not metrics:
|
||||
gui_label(rl.Rectangle(col4_x, rect.y + 38, col4_w - 16, 26), tr("Standard"), 18, AetherListColors.MUTED, FontWeight.MEDIUM)
|
||||
else:
|
||||
draw_status_badges(col4_x, rect.y + 37, metrics, PANEL_STYLE)
|
||||
|
||||
def _measure_content_height(self, width: float) -> float:
|
||||
self._check_rebuild_grid()
|
||||
cs = starpilot_state.car_state
|
||||
RELOCATED_HEADER_HEIGHT = 112.0
|
||||
|
||||
# Left Column heights
|
||||
identity_rows = 2
|
||||
if cs.isToyota:
|
||||
identity_rows += 2
|
||||
identity_h = self._section_block_height(self._section_height(identity_rows, ROW_HEIGHT))
|
||||
|
||||
steering_rows = self._build_steering_rows()
|
||||
steering_h = self._section_block_height(self._section_height(len(steering_rows), ROW_HEIGHT))
|
||||
|
||||
left_h = identity_h + SECTION_GAP + steering_h
|
||||
|
||||
# Right Column/Features height
|
||||
tiles_height = 0.0
|
||||
if self._toggle_grid.tiles:
|
||||
if not self._uses_two_columns(width):
|
||||
self._toggle_grid._columns = 3
|
||||
avail_w = width - 24
|
||||
tiles_content_h = self.measure_page_grid_height(self._toggle_grid, avail_w)
|
||||
tiles_height = SECTION_GAP + self._section_block_height(tiles_content_h + 24)
|
||||
|
||||
if self._uses_two_columns(width):
|
||||
column_w = self._column_width(width)
|
||||
tiles_content_h = self.measure_page_grid_height(self._toggle_grid, column_w - 24)
|
||||
right_natural_container_h = tiles_content_h + 24
|
||||
left_natural_content_h = identity_h + SECTION_GAP + SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP + steering_h + RELOCATED_HEADER_HEIGHT
|
||||
|
||||
max_container_h = max(left_natural_content_h, right_natural_container_h)
|
||||
self._vehicle_max_container_h = max_container_h
|
||||
self._vehicle_section_gap = max(SECTION_GAP, (max_container_h - RELOCATED_HEADER_HEIGHT) - (identity_h + steering_h + SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP))
|
||||
|
||||
return self._compute_two_column_height(max_container_h + SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP)
|
||||
return left_h + tiles_height + RELOCATED_HEADER_HEIGHT
|
||||
|
||||
def _draw_scroll_content(self, rect: rl.Rectangle, width: float):
|
||||
self._interactive_rects.clear()
|
||||
y = rect.y + self._scroll_offset
|
||||
self._draw_panel_content(y, rect.x, width)
|
||||
|
||||
def _draw_panel_content(self, y: float, x: float, width: float):
|
||||
self._check_rebuild_grid()
|
||||
cs = starpilot_state.car_state
|
||||
|
||||
# Relocated Header elements drawn at the top of the left column
|
||||
col_w = self._column_width(width) if self._uses_two_columns(width) else width
|
||||
|
||||
# 1. Draw Summary Card
|
||||
summary_rect = rl.Rectangle(x, y, col_w, 100.0)
|
||||
self._draw_summary_card(summary_rect)
|
||||
|
||||
RELOCATED_HEADER_HEIGHT = 112.0
|
||||
y += RELOCATED_HEADER_HEIGHT
|
||||
|
||||
identity_rows = [
|
||||
{"target_id": "select:CarMake", "type": "select", "title": tr("Car Make"),
|
||||
"get_value": self._controller._get_display_make, "pill_width": 160,
|
||||
"is_enabled": self._controller._params.get_bool("ForceFingerprint")},
|
||||
{"target_id": "select:CarModel", "type": "select", "title": tr("Car Model"),
|
||||
"get_value": self._controller._get_display_model, "pill_width": 160,
|
||||
"is_enabled": self._controller._params.get_bool("ForceFingerprint")},
|
||||
]
|
||||
if cs.isToyota:
|
||||
identity_rows.append({"target_id": "select:LockDoorsTimer", "type": "select",
|
||||
"title": tr("Lock Doors Timer"),
|
||||
"get_value": lambda: _lock_doors_timer_labels().get(float(self._controller._params.get_int("LockDoorsTimer")), f"{self._controller._params.get_int('LockDoorsTimer')}s"),
|
||||
"pill_width": 100})
|
||||
identity_rows.append({"target_id": "select:ClusterOffset", "type": "select",
|
||||
"title": tr("Dashboard Speed Offset"),
|
||||
"get_value": lambda: f"{self._controller._params.get_float('ClusterOffset'):.3f}x",
|
||||
"pill_width": 120})
|
||||
|
||||
steering_rows = self._build_steering_rows()
|
||||
|
||||
if self._uses_two_columns(width):
|
||||
column_w = self._column_width(width)
|
||||
|
||||
# Left Column: Vehicle Identity & Steering Controls
|
||||
curr_y = y
|
||||
draw_section_header(rl.Rectangle(x, curr_y, column_w, SECTION_HEADER_HEIGHT), tr("Vehicle Identity"), style=PANEL_STYLE)
|
||||
curr_y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
container_rect = rl.Rectangle(x, curr_y, column_w, len(identity_rows) * ROW_HEIGHT)
|
||||
draw_list_group_shell(container_rect, style=PANEL_STYLE)
|
||||
for index, row in enumerate(identity_rows):
|
||||
row_rect = rl.Rectangle(x, curr_y + index * ROW_HEIGHT, column_w, ROW_HEIGHT)
|
||||
self._draw_row(row_rect, row, is_last=index == len(identity_rows) - 1)
|
||||
curr_y += len(identity_rows) * ROW_HEIGHT
|
||||
|
||||
curr_y += self._vehicle_section_gap
|
||||
draw_section_header(rl.Rectangle(x, curr_y, column_w, SECTION_HEADER_HEIGHT), tr("Steering Controls"), style=PANEL_STYLE)
|
||||
curr_y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
container_rect = rl.Rectangle(x, curr_y, column_w, len(steering_rows) * ROW_HEIGHT)
|
||||
draw_list_group_shell(container_rect, style=PANEL_STYLE)
|
||||
for index, row in enumerate(steering_rows):
|
||||
row_rect = rl.Rectangle(x, curr_y + index * ROW_HEIGHT, column_w, ROW_HEIGHT)
|
||||
self._draw_row(row_rect, row, is_last=index == len(steering_rows) - 1)
|
||||
|
||||
# Right Column: Features
|
||||
if self._toggle_grid.tiles:
|
||||
rx = x + column_w + self.COLUMN_GAP
|
||||
self._draw_two_column_tile_grid(self._toggle_grid, rx, y - RELOCATED_HEADER_HEIGHT, column_w, self._vehicle_max_container_h, title=tr("Features"), style=PANEL_STYLE)
|
||||
else:
|
||||
# Single Column Stacked Layout
|
||||
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), tr("Vehicle Identity"), style=PANEL_STYLE)
|
||||
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
container_rect = rl.Rectangle(x, y, width, len(identity_rows) * ROW_HEIGHT)
|
||||
draw_list_group_shell(container_rect, style=PANEL_STYLE)
|
||||
for index, row in enumerate(identity_rows):
|
||||
row_rect = rl.Rectangle(x, y + index * ROW_HEIGHT, width, ROW_HEIGHT)
|
||||
self._draw_row(row_rect, row, is_last=index == len(identity_rows) - 1)
|
||||
y += len(identity_rows) * ROW_HEIGHT
|
||||
|
||||
y += SECTION_GAP
|
||||
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), tr("Steering Controls"), style=PANEL_STYLE)
|
||||
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
container_rect = rl.Rectangle(x, y, width, len(steering_rows) * ROW_HEIGHT)
|
||||
draw_list_group_shell(container_rect, style=PANEL_STYLE)
|
||||
for index, row in enumerate(steering_rows):
|
||||
row_rect = rl.Rectangle(x, y + index * ROW_HEIGHT, width, ROW_HEIGHT)
|
||||
self._draw_row(row_rect, row, is_last=index == len(steering_rows) - 1)
|
||||
y += len(steering_rows) * ROW_HEIGHT
|
||||
|
||||
if self._toggle_grid.tiles:
|
||||
y += SECTION_GAP
|
||||
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), tr("Features"), style=PANEL_STYLE)
|
||||
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
|
||||
|
||||
self._toggle_grid._columns = 3
|
||||
avail_w = width - 24
|
||||
tiles_content_h = self.measure_page_grid_height(self._toggle_grid, avail_w)
|
||||
|
||||
draw_list_group_shell(rl.Rectangle(x, y, width, tiles_content_h + 24), style=PANEL_STYLE)
|
||||
self._render_page_grid(self._toggle_grid, rl.Rectangle(x + 12, y + 12, avail_w, tiles_content_h))
|
||||
|
||||
def _draw_row(self, rect: rl.Rectangle, row: dict, is_last: bool):
|
||||
target_id = row["target_id"]
|
||||
is_enabled = row.get("is_enabled", True)
|
||||
hovered, pressed = self._interactive_state(target_id, rect) if is_enabled else (False, False)
|
||||
row_type = row.get("type", "toggle")
|
||||
|
||||
if row_type == "toggle":
|
||||
draw_settings_list_row(
|
||||
rect, title=row["title"], subtitle=row.get("subtitle", ""),
|
||||
toggle_value=row["get_state"](), hovered=hovered, pressed=pressed,
|
||||
is_last=is_last, show_chevron=False, title_size=34, subtitle_size=22,
|
||||
style=PANEL_STYLE,
|
||||
)
|
||||
elif row_type == "select":
|
||||
draw_selection_list_row(
|
||||
rect, title=row["title"], subtitle=row.get("subtitle", ""),
|
||||
action_text=row["get_value"](), hovered=hovered and is_enabled,
|
||||
pressed=pressed and is_enabled, is_last=is_last,
|
||||
action_width=188, action_pill=True,
|
||||
action_pill_width=row.get("pill_width", 108), action_pill_height=44,
|
||||
title_size=34, subtitle_size=22, action_text_size=18,
|
||||
row_separator=PANEL_STYLE.divider_color,
|
||||
action_fill=PANEL_STYLE.current_fill if is_enabled else with_alpha(PANEL_STYLE.current_fill, 120),
|
||||
action_border=PANEL_STYLE.current_border if is_enabled else with_alpha(PANEL_STYLE.current_border, 100),
|
||||
action_text_color=AetherListColors.HEADER if is_enabled else AetherListColors.MUTED,
|
||||
)
|
||||
elif row_type == "info":
|
||||
draw_settings_list_row(
|
||||
rect, title=row["title"], value=row["get_value"](),
|
||||
hovered=False, pressed=False, is_last=is_last,
|
||||
show_chevron=False, title_size=34, subtitle_size=22,
|
||||
style=PANEL_STYLE,
|
||||
)
|
||||
|
||||
def _build_steering_rows(self) -> list[dict]:
|
||||
cs = starpilot_state.car_state
|
||||
rows = []
|
||||
for key in ("DistanceButtonControl", "LongDistanceButtonControl", "VeryLongDistanceButtonControl"):
|
||||
rows.append({"target_id": f"select:{key}", "type": "select", "title": tr(self._controller._action_title(key)),
|
||||
"get_value": lambda k=key: self._controller._get_action_name(k), "pill_width": 140})
|
||||
if cs.isBolt and cs.hasPedal and self._controller._params.get_bool("RemapCancelToDistance"):
|
||||
for key in ("CancelButtonControl", "LongCancelButtonControl", "VeryLongCancelButtonControl"):
|
||||
rows.append({"target_id": f"select:{key}", "type": "select", "title": tr(self._controller._action_title(key)),
|
||||
"get_value": lambda k=key: self._controller._get_action_name(k), "pill_width": 140})
|
||||
if not cs.isSubaru:
|
||||
rows.append({"target_id": "select:LKASButtonControl", "type": "select", "title": tr("LKAS Button"),
|
||||
"get_value": lambda: self._controller._get_action_name("LKASButtonControl"), "pill_width": 140})
|
||||
rows.append({"target_id": "select:MainCruiseButtonControl", "type": "select", "title": tr("CC Main Button"),
|
||||
"get_value": lambda: self._controller._get_action_name("MainCruiseButtonControl"), "pill_width": 140})
|
||||
if cs.hasModeStarButtons:
|
||||
for key in ("ModeButtonControl", "LongModeButtonControl", "VeryLongModeButtonControl",
|
||||
"StarButtonControl", "LongStarButtonControl", "VeryLongStarButtonControl"):
|
||||
rows.append({"target_id": f"select:{key}", "type": "select", "title": tr(self._controller._action_title(key)),
|
||||
"get_value": lambda k=key: self._controller._get_action_name(k), "pill_width": 140})
|
||||
return rows
|
||||
|
||||
|
||||
class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
def __init__(self):
|
||||
@@ -521,7 +439,6 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
self._make_options, self._models_by_make, self._models_by_value, self._make_by_model = get_fingerprint_catalog()
|
||||
self._manager_view = VehicleSettingsManagerView(self)
|
||||
|
||||
|
||||
def _action_title(self, key: str) -> str:
|
||||
titles = {
|
||||
"CancelButtonControl": "Cancel Button",
|
||||
@@ -689,7 +606,8 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
self._params.put_int("LockDoorsTimer", int(val))
|
||||
|
||||
gui_app.push_widget(AetherSliderDialog(tr("Lock Doors Timer"), 0, 300, 5,
|
||||
self._params.get_int("LockDoorsTimer"), on_close, labels=_lock_doors_timer_labels(), color=PANEL_STYLE.accent))
|
||||
self._params.get_int("LockDoorsTimer"), on_close,
|
||||
labels=_lock_doors_timer_labels(), color=PANEL_STYLE.accent))
|
||||
|
||||
def _show_offset_selector(self):
|
||||
def on_close(res, val):
|
||||
@@ -697,7 +615,8 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
self._params.put_float("ClusterOffset", float(val))
|
||||
|
||||
gui_app.push_widget(AetherSliderDialog(tr("Dashboard Speed Offset"), 1.000, 1.050, 0.001,
|
||||
self._params.get_float("ClusterOffset"), on_close, unit="x", color=PANEL_STYLE.accent))
|
||||
self._params.get_float("ClusterOffset"), on_close,
|
||||
unit="x", color=PANEL_STYLE.accent))
|
||||
|
||||
def _get_display_make(self) -> str:
|
||||
make = self._params.get("CarMake") or ""
|
||||
@@ -706,7 +625,7 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
model = self._params.get("CarModel") or ""
|
||||
if model:
|
||||
return self._make_by_model.get(model, format_fingerprint_value(model.split("_", 1)[0]))
|
||||
return tr("None")
|
||||
return tr("Auto") if not self._params.get_bool("ForceFingerprint") else tr("None")
|
||||
|
||||
def _get_display_model(self) -> str:
|
||||
selected = self._get_selected_model_option()
|
||||
@@ -721,7 +640,7 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
return self._models_by_value[model].button_label
|
||||
if model:
|
||||
return format_fingerprint_value(model)
|
||||
return tr("None")
|
||||
return tr("Auto") if not self._params.get_bool("ForceFingerprint") else tr("None")
|
||||
|
||||
def _get_selected_model_option(self) -> FingerprintModelOption | None:
|
||||
model = self._params.get("CarModel") or ""
|
||||
|
||||
Reference in New Issue
Block a user