[TIZI/TICI] ui: label the Model Status slots small and big and scroll long names

This commit is contained in:
Jason Wen
2026-08-26 04:00:27 -04:00
parent 9e08f374d1
commit 9348d4d7f6
3 changed files with 26 additions and 15 deletions
@@ -13,7 +13,7 @@ from openpilot.cereal import custom
from openpilot.sunnypilot.models.helpers import ACTIVE_BUNDLE_KEYS, get_selected_bundle, resolve_bundle_by_ref
from openpilot.common.constants import CV
from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.selfdrive.ui.sunnypilot.model_info import active_source, bundles_for_source, default_model_name, model_info
from openpilot.selfdrive.ui.sunnypilot.model_info import active_source, bundles_for_source, default_model, default_model_name, model_info
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.widgets import DialogResult, Widget
@@ -163,26 +163,23 @@ class ModelsLayout(Widget):
state = self._download_row_state(progresses, bundle.internalName)
if queued := self._queued_name(bundle.ref):
state["name"] += f" · {queued} {tr('queued')}"
state["name"] += f" | {queued} {tr('queued')}"
self.download_item.action_item.update(**state)
self._downloading = self.download_item.action_item.downloading
ds = custom.ModelManagerSP.DownloadStatus
self._verifying = any(getattr(p.status, 'raw', p.status) == ds.verifying for p in progresses)
def _slot_segments(self):
"""One segment per slot: name, downloaded check or empty-slot mark, active in green."""
"""small and big slots side by side; the active side is green, an empty slot shows its default."""
active = active_source()
segments = []
for source in ("qcom", "usbgpu"):
for source, label in (("qcom", tr("small")), ("usbgpu", tr("big"))):
if segments:
segments.append(("|", rl.GRAY, None, None))
bundle = get_selected_bundle(ui_state.params, source)
name = bundle.internalName if bundle else tr("Default")
if source == active:
name += f" ({tr('active')})"
color = ON_COLOR if source == active else rl.GRAY
if bundle:
segments.append((name, color, "icons/checkmark.png", None))
else:
segments.append((name, color, "icons/circled_slash.png", rl.WHITE))
name = bundle.internalName if bundle else default_model(source)
segments.append((label, rl.GRAY, None, None))
segments.append((name, ON_COLOR if source == active else rl.LIGHTGRAY, "icons/checkmark.png", None))
return segments
def _queued_name(self, current_ref):
@@ -22,8 +22,12 @@ def bundles_for_source(source: str):
return get_cached_bundles(ui_state.params, source)
def default_model(source: str) -> str:
return DEFAULT_BIG_MODEL if source == 'usbgpu' else DEFAULT_MODEL
def default_model_name(source: str) -> str:
return f"{DEFAULT_BIG_MODEL if source == 'usbgpu' else DEFAULT_MODEL} (Default)"
return f"{default_model(source)} (Default)"
def model_info() -> tuple[str, str, str]:
@@ -25,6 +25,7 @@ ICON_PADDING = 12
BAR_WIDTH = 1100
BAR_HEIGHT = 20
SEGMENT_GAP = 24
SEGMENT_NAME_MAX_WIDTH = 380
BAR_GAP = 16
BAR_RADIUS = BAR_HEIGHT / 2
CAPSULE_POINTS = 24
@@ -47,6 +48,7 @@ class DownloadStatusAction(ItemAction):
self.name = ""
self.status_text = ""
self.segments: list[tuple[str, rl.Color, str | None, rl.Color | None]] | None = None
self._segment_labels: list[UnifiedLabel] = []
self.downloading = False
self.text_color = rl.GRAY
self.icon: str | None = None
@@ -99,7 +101,7 @@ class DownloadStatusAction(ItemAction):
"""[(segment, text width, total width incl. icon and gap)]"""
out = []
for i, seg in enumerate(self.segments or []):
text_width = measure_text_cached(self._font, seg[0], FONT_SIZE).x
text_width = min(measure_text_cached(self._font, seg[0], FONT_SIZE).x, SEGMENT_NAME_MAX_WIDTH)
total = text_width + (ICON_PADDING + ICON_SIZE if seg[2] else 0) + (SEGMENT_GAP if i else 0)
out.append((seg, text_width, total))
return out
@@ -182,12 +184,20 @@ class DownloadStatusAction(ItemAction):
def _render_segments(self, rect: rl.Rectangle):
measured = self._measured_segments()
while len(self._segment_labels) < len(measured):
self._segment_labels.append(UnifiedLabel("", font_size=FONT_SIZE, max_width=SEGMENT_NAME_MAX_WIDTH,
scroll=True, wrap_text=False))
x = rect.x + rect.width - sum(total for _, _, total in measured)
for i, ((text, color, icon, icon_color), text_width, _) in enumerate(measured):
if i:
x += SEGMENT_GAP
label = self._segment_labels[i]
if label.text != text:
label.set_text(text)
label.set_text_color(color)
text_height = measure_text_cached(self._font, text, FONT_SIZE).y
rl.draw_text_ex(self._font, text, rl.Vector2(x, rect.y + (rect.height - text_height) / 2), FONT_SIZE, 0, color)
label.set_position(x, rect.y + (rect.height - text_height) / 2)
label.render()
x += text_width
if icon:
texture = gui_app.texture(icon, ICON_SIZE, ICON_SIZE, keep_aspect_ratio=True)