This commit is contained in:
firestarsdog
2026-10-03 15:30:50 -04:00
parent 12947fd616
commit 7b06ec2ae6
25 changed files with 370 additions and 96 deletions
+11 -8
View File
@@ -5,6 +5,7 @@ from collections.abc import Callable
from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout
from openpilot.selfdrive.ui.layouts.settings.starpilot.main_panel import StarPilotLayout
from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import draw_text_fit_common
from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout
from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
@@ -24,6 +25,7 @@ CLOSE_BTN_SIZE = 200
CLOSE_ICON_SIZE = 70
NAV_BTN_HEIGHT = 110
PANEL_MARGIN = 10
COLLAPSED_TAB_WIDTH = 64
# Colors
SIDEBAR_COLOR = rl.BLACK
@@ -126,7 +128,7 @@ class SettingsLayout(Widget):
original_events = list(gui_app.mouse_events)
if not self._sidebar_expanded:
tab_zone = rl.Rectangle(rect.x, rect.y + 581 - 70, 40, 140)
tab_zone = rl.Rectangle(rect.x, rect.y + 581 - 70, COLLAPSED_TAB_WIDTH, 140)
gui_app.mouse_events[:] = [e for e in original_events if not rl.check_collision_point_rec(e.pos, tab_zone)]
self._draw_current_panel(panel_rect)
@@ -161,13 +163,14 @@ class SettingsLayout(Widget):
# Unified Protruding Edge Tab (Expand/Collapse toggle)
tab_cy = int(rect.y + 581)
tab_h = 140
tab_w = 70
tab_x = rect.x - 30 # Leaves exactly 40px protruding onto the screen
visible_w = 40 if self._sidebar_expanded else COLLAPSED_TAB_WIDTH
tab_w = visible_w + 30
tab_x = rect.x - 30
tab_y = tab_cy - (tab_h / 2)
tab_rect = rl.Rectangle(tab_x, tab_y, tab_w, tab_h)
# Hit zone is the visible portion on screen
self._collapse_btn_rect = rl.Rectangle(rect.x, tab_y, 40, tab_h)
self._collapse_btn_rect = rl.Rectangle(rect.x, tab_y, visible_w, tab_h)
# Interaction state
is_pressed = False
@@ -192,7 +195,7 @@ class SettingsLayout(Widget):
rl.draw_rectangle_rounded_lines_ex(tab_rect, 0.5, 30, 2.0, tab_border)
# Chevron properly centered on the *visible* portion of the tab
chevron_x = rect.x + 20
chevron_x = rect.x + visible_w / 2
self._draw_chevron(chevron_x, tab_cy, not self._sidebar_expanded, rl.Color(255, 255, 255, 255), size=24, bloom=True)
if self._sidebar_expanded:
@@ -234,8 +237,8 @@ class SettingsLayout(Widget):
# Draw button text (right-aligned)
panel_name = tr(panel_info.name)
text_size = measure_text_cached(self._font_medium, panel_name, 65)
text_pos = rl.Vector2(button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2)
rl.draw_text_ex(self._font_medium, panel_name, rl.Vector2(round(text_pos.x), round(text_pos.y)), 65, 0, text_color)
text_pos = rl.Vector2(button_rect.x, button_rect.y + (button_rect.height - text_size.y) / 2)
draw_text_fit_common(self._font_medium, panel_name, text_pos, button_rect.width, 65, align_right=True, color=text_color)
# Store button rect for click detection
panel_info.button_rect = button_rect
@@ -253,7 +256,7 @@ class SettingsLayout(Widget):
if not self._sidebar_expanded:
# Only record swipe/tap start when touch is within the 10px left margin OR directly on the protruding tab
gesture_zone = rl.Rectangle(self._rect.x, self._rect.y, 10, self._rect.height)
tab_zone = rl.Rectangle(self._rect.x, self._rect.y + 581 - 70, 40, 140)
tab_zone = rl.Rectangle(self._rect.x, self._rect.y + 581 - 70, COLLAPSED_TAB_WIDTH, 140)
if rl.check_collision_point_rec(mouse_pos, gesture_zone) or rl.check_collision_point_rec(mouse_pos, tab_zone):
self._swipe_start = mouse_pos
else:
@@ -3013,7 +3013,10 @@ def draw_selection_list_row(
if action_text:
if action_pill:
available_w = max(96.0, action_rect.width - 28)
chip_w = min(available_w, action_pill_width) if action_pill_width is not None else min(available_w, max(96.0, 42 + len(action_text) * 9))
pill_width = action_pill_width
if pill_width is None:
pill_width = max(96.0, math.ceil(measure_text_cached(title_font, action_text, action_text_size).x) + 24)
chip_w = min(available_w, pill_width)
chip_h = min(float(action_pill_height), max(36.0, action_rect.height - 28))
chip_rect = rl.Rectangle(action_rect.x + action_rect.width - chip_w - 18, action_rect.y + (action_rect.height - chip_h) / 2, chip_w, chip_h)
draw_action_pill(
@@ -4023,7 +4026,7 @@ class HubTile(AetherTile):
icon_scale = min(0.80, max(0.56, text_scale * 0.72))
icon_h = CUSTOM_ICON_BASE_SIZE * CUSTOM_ICON_SCALE_MULT * icon_scale
total_h = icon_h + (gap if icon_h > 0 else 0) + title_size + (gap if desc_to_render else 0) + desc_size
total_h = icon_h + (gap if icon_h > 0 else 0) + (title_size + desc_size) * FONT_SCALE + (gap if desc_to_render else 0)
content_top = ry + max(0, (rh - total_h) / 2)
if self.custom_icon_key:
@@ -4037,7 +4040,7 @@ class HubTile(AetherTile):
draw_text_fit_common(self._font_title, title_text,
rl.Vector2(rx + content_pad, content_top),
max_w, title_size, align_center=True, color=rl.WHITE)
content_top += title_size
content_top += title_size * FONT_SCALE
if desc_to_render:
content_top += gap
@@ -555,23 +555,23 @@ class StarPilotLongitudinalLayout(_SettingsPage):
visible=adv),
SettingRow("MaxAccel", "value", tr_noop("Maximum Acceleration"),
subtitle=tr_noop("Strongest acceleration openpilot is allowed to command."),
get_value=lambda: f"{self._params.get_float('MaxDesiredAcceleration'):.1f}m/s" if self._params.get_float("MaxDesiredAcceleration") is not None else "N/A",
on_click=lambda: self._show_slider("MaxDesiredAcceleration", 0.1, 4.0, step=0.1, unit="m/s", value_type="float"),
get_value=lambda: f"{self._params.get_float('MaxDesiredAcceleration'):.1f}m/s²" if self._params.get_float("MaxDesiredAcceleration") is not None else "N/A",
on_click=lambda: self._show_slider("MaxDesiredAcceleration", 0.1, 4.0, step=0.1, unit="m/s²", value_type="float"),
visible=adv),
SettingRow("StartAccel", "value", tr_noop("Start Acceleration"),
subtitle=tr_noop("Extra acceleration when moving away from a stop."),
get_value=lambda: f"{self._params.get_float('StartAccel'):.2f}m/s",
on_click=lambda: self._show_slider("StartAccel", 0.0, 4.0, step=0.01, unit="m/s", value_type="float"),
get_value=lambda: f"{self._params.get_float('StartAccel'):.2f}m/s²",
on_click=lambda: self._show_slider("StartAccel", 0.0, 4.0, step=0.01, unit="m/s²", value_type="float"),
visible=adv),
SettingRow("StopAccel", "value", tr_noop("Stop Acceleration"),
subtitle=tr_noop("Brake force to hold the vehicle at a complete stop."),
get_value=lambda: f"{self._params.get_float('StopAccel'):.2f}m/s",
on_click=lambda: self._show_slider("StopAccel", -4.0, 0.0, step=0.01, unit="m/s", value_type="float"),
get_value=lambda: f"{self._params.get_float('StopAccel'):.2f}m/s²",
on_click=lambda: self._show_slider("StopAccel", -4.0, 0.0, step=0.01, unit="m/s²", value_type="float"),
visible=adv),
SettingRow("StoppingRate", "value", tr_noop("Stopping Rate"),
subtitle=tr_noop("How quickly braking ramps up to bring the car to a stop."),
get_value=lambda: f"{self._params.get_float('StoppingDecelRate'):.3f}m/s",
on_click=lambda: self._show_slider("StoppingDecelRate", 0.001, 1.0, step=0.001, unit="m/s", value_type="float"),
get_value=lambda: f"{self._params.get_float('StoppingDecelRate'):.3f}m/s²/s",
on_click=lambda: self._show_slider("StoppingDecelRate", 0.001, 1.0, step=0.001, unit="m/s²/s", value_type="float"),
visible=self._show_stop_tuning_values),
SettingRow("StartSpeed", "value", tr_noop("Start Speed"),
subtitle=tr_noop("Speed where openpilot exits the stopped state."),
@@ -674,7 +674,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
self._curve_speed_controller_rows = [
SettingRow("CalibratedLatAccel", "value", tr_noop("Calibrated Lateral Accel"),
subtitle=tr_noop("The learned lateral acceleration from collected driving data. Higher values allow faster cornering."),
get_value=lambda: f"{self._params.get_float('CalibratedLateralAcceleration'):.2f} m/s",
get_value=lambda: f"{self._params.get_float('CalibratedLateralAcceleration'):.2f} m/s²",
on_click=None,
visible=csc_on),
SettingRow("CalibrationProgress", "value", tr_noop("Calibration Progress"),
@@ -847,7 +847,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
self._sub_panels["csc"] = AetherSettingsView(
self,
[SettingSection(tr("Curve Speed Controller"), self._curve_speed_controller_rows)],
[SettingSection(title="", rows=self._curve_speed_controller_rows)],
header_title=tr("Curve Speed Controller"),
header_subtitle=tr("Configure speed control on curves and reset collected calibration data."),
parent_toggle=pt_csc,
@@ -350,7 +350,7 @@ class MapsManagerView(PanelManagerView):
# Subtitle / Body Progress Description (26pt)
gui_text_box(
rl.Rectangle(content_x, rect.y + 58, summary_w, 54),
rl.Rectangle(content_x, rect.y + 58, summary_w, 68),
self._controller._progress_body(),
26,
AetherListColors.SUBTEXT,
@@ -452,9 +452,10 @@ class StarPilotMapsLayout(_SettingsPage):
)
self._schedule_button = self._child(
AetherButton(
lambda: tr("Update: {}").format(_localized_schedule_label(self._params.get('PreferredSchedule'))),
self._schedule_action_label,
self._on_schedule,
emphasized=False,
font_size=26,
)
)
@@ -788,6 +789,13 @@ class StarPilotMapsLayout(_SettingsPage):
else:
self._on_download()
def _schedule_action_label(self) -> str:
return {
"Manually": tr("Auto: Off"),
"Weekly": tr("Auto: Weekly"),
"Monthly": tr("Auto: Monthly"),
}[schedule_label(self._params.get("PreferredSchedule"))]
def _on_schedule(self):
localized_options = [(value, tr(label)) for value, label in MAP_SCHEDULE_LABELS.items()]
options = [label for _, label in localized_options]
@@ -10,9 +10,10 @@ from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.lib.scroll_panel2 import GuiScrollPanel2
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets import Widget, DialogResult
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
AetherListColors, AetherListMetrics, AetherScrollbar,
draw_hud_background, draw_soft_card, draw_action_pill, draw_selection_list_row,
draw_hud_background, draw_action_pill, draw_selection_list_row,
draw_empty_state_card, draw_busy_ring, draw_list_scroll_fades,
draw_rounded_fill, draw_rounded_stroke, draw_text_fit_common,
draw_download_icon,
@@ -167,9 +168,6 @@ class SimpleDownloadManager(Widget):
self._slug_map: dict[str, str] = {}
self._item_rects: dict[str, rl.Rectangle] = {}
self._pill_rects: list[rl.Rectangle] = []
self._confirm_target: str | None = None
self._confirm_yes_rect = rl.Rectangle(0, 0, 0, 0)
self._confirm_no_rect = rl.Rectangle(0, 0, 0, 0)
self._close_rect = rl.Rectangle(0, 0, 0, 0)
self._cancel_rect = rl.Rectangle(0, 0, 0, 0)
self._info_message = ""
@@ -234,7 +232,6 @@ class SimpleDownloadManager(Widget):
_add_to_downloadable_list(self.params, self.downloadable_list_param, display_name)
self._info_message = f"Deleted \"{display_name}\""
self._info_message_until = time.monotonic() + 2.5
self._confirm_target = None
self._refresh_list()
def _start_download(self, display_name: str):
@@ -266,12 +263,6 @@ class SimpleDownloadManager(Widget):
for i, prect in enumerate(self._pill_rects):
if rl.check_collision_point_rec(pos, prect):
return f"mode:{i}"
if self._confirm_target is not None:
if rl.check_collision_point_rec(pos, self._confirm_yes_rect):
return "confirm_yes"
if rl.check_collision_point_rec(pos, self._confirm_no_rect):
return "confirm_no"
return None
if self._downloading:
if rl.check_collision_point_rec(pos, self._cancel_rect):
return "cancel_download"
@@ -301,7 +292,6 @@ class SimpleDownloadManager(Widget):
if mode != self._active_mode:
self._active_mode = mode
self._scroll_offset = 0.0
self._confirm_target = None
self._refresh_list()
elif target.startswith("item:"):
idx = int(target.split(":")[1])
@@ -310,13 +300,15 @@ class SimpleDownloadManager(Widget):
if self._active_mode == self.MODE_SELECT:
self._select_asset(item)
elif self._active_mode == self.MODE_DELETE:
self._confirm_target = item
def on_result(result: DialogResult):
if result == DialogResult.CONFIRM:
self._delete_asset(item)
gui_app.push_widget(ConfirmDialog(
tr("Delete \"{name}\"?").format(name=item), tr("DELETE"), callback=on_result,
))
elif self._active_mode == self.MODE_DOWNLOAD:
self._start_download(item)
elif target == "confirm_yes" and self._confirm_target is not None:
self._delete_asset(self._confirm_target)
elif target == "confirm_no":
self._confirm_target = None
elif target == "cancel_download":
self._cancel_download()
@@ -450,7 +442,9 @@ class SimpleDownloadManager(Widget):
# Measure content
n_items = len(self._list_items)
content_height = float(n_items * self.ITEM_HEIGHT)
prog_h = 140
progress_space = prog_h + 40 if self._downloading else 0
content_height = float(n_items * self.ITEM_HEIGHT + progress_space)
self._scroll_panel.set_enabled(True)
self._scroll_offset = self._scroll_panel.update(scroll_rect, max(content_height, scroll_rect.height))
@@ -475,7 +469,6 @@ class SimpleDownloadManager(Widget):
if self._downloading:
# Download progress display
prog_h = 140
prog_rect = rl.Rectangle(scroll_rect.x + 20, scroll_rect.y + self._scroll_offset + 20,
scroll_rect.width - 40, prog_h)
draw_rounded_fill(prog_rect, with_alpha(rl.Color(255, 255, 255, 8), 255), radius_px=16)
@@ -508,49 +501,7 @@ class SimpleDownloadManager(Widget):
AetherListColors.HEADER, font_size=24,
)
item_y += prog_h + 40
if self._confirm_target is not None:
# Confirm overlay
confirm_w = min(720, scroll_rect.width - 60)
confirm_h = 220
confirm_rect = rl.Rectangle(
scroll_rect.x + (scroll_rect.width - confirm_w) / 2,
scroll_rect.y + self._scroll_offset + (scroll_rect.height - confirm_h) / 2,
confirm_w, confirm_h,
)
draw_soft_card(confirm_rect, rl.Color(20, 19, 25, 255), with_alpha(AetherListColors.PANEL_BORDER, 160),
radius=0.03, segments=18)
confirm_font = gui_app.font(FontWeight.MEDIUM)
confirm_msg = tr("Delete \"{name}\"?").format(name=self._confirm_target)
draw_text_fit_common(
confirm_font, confirm_msg,
rl.Vector2(confirm_rect.x + 24, confirm_rect.y + 32),
confirm_rect.width - 48, 32,
align_center=True, color=AetherListColors.HEADER,
)
btn_w = (confirm_rect.width - 60) / 2
btn_y = confirm_rect.y + confirm_rect.height - 76
btn_h = 52
self._confirm_no_rect = snap_rect(rl.Rectangle(confirm_rect.x + 20, btn_y, btn_w, btn_h))
self._confirm_yes_rect = snap_rect(rl.Rectangle(confirm_rect.x + confirm_rect.width - btn_w - 20, btn_y, btn_w, btn_h))
no_hovered = self._pressed_target == "confirm_no"
yes_hovered = self._pressed_target == "confirm_yes"
draw_action_pill(self._confirm_no_rect, tr("CANCEL"),
with_alpha(rl.Color(255, 255, 255, 14 if no_hovered else 8), 255),
with_alpha(rl.Color(255, 255, 255, 36), 255),
AetherListColors.SUBTEXT, font_size=24)
draw_action_pill(self._confirm_yes_rect, tr("DELETE"),
with_alpha(AetherListColors.DANGER, 60 if yes_hovered else 36),
with_alpha(AetherListColors.DANGER, 120),
AetherListColors.HEADER, font_size=24)
item_y += confirm_h + 20
item_y += progress_space
# Draw list items
for idx, item in enumerate(self._list_items):
@@ -541,7 +541,7 @@ class ButtonActionComboDialog(Widget):
title_size = 64
ts = measure_text_cached(self._font_title, self._title, title_size)
rl.draw_text_ex(self._font_title, self._title,
rl.Vector2(int(dx + (dialog_w - ts.x) / 2), int(dy + 87)),
rl.Vector2(int(dx + (dialog_w - ts.x) / 2), int(dy + 40)),
title_size, 0, rl.WHITE)
font_label = gui_app.font(FontWeight.MEDIUM)
@@ -10,6 +10,7 @@ import pyray as rl
from openpilot.common.params import UnknownKeyName
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import FONT_SCALE, FontWeight, gui_app
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets import Widget
@@ -376,7 +377,7 @@ class NavigationCardRenderer(Widget):
)
then_x = divider_x + 15
then_label = "Then"
then_label = tr("Then")
then_font_size = 36
then_size = measure_text_cached(self._font_medium, then_label, then_font_size)
then_label_x = then_x + (then_section_width - 23 - then_size.x) / 2
+31
View File
@@ -249,6 +249,37 @@ def test_empty_state_body_wraps_within_card(height, body_size, body_height, body
assert body_labels[-1][1].endswith("...")
@pytest.mark.parametrize("action_text", ["DELETE", "DOWNLOAD", "Current", "Reset", "Set Key"])
def test_auto_action_pill_keeps_readable_text_inside_action_rail(action_text):
mod = _import_aethergrid()
mod.FONT_SCALE = 1.242
draws = []
def measure(_font, text, size, spacing=0):
return types.SimpleNamespace(x=len(text) * size * mod.FONT_SCALE * 0.65, y=size * mod.FONT_SCALE)
with patch.object(mod, "measure_text_cached", measure), \
patch.object(mod.rl, "draw_text_ex", lambda font, text, pos, size, spacing, color: draws.append((text, pos, size))):
pill = mod.draw_selection_list_row(mod.rl.Rectangle(0, 0, 1500, 128), title="Asset", action_text=action_text,
action_pill=True, action_text_size=26)
text, pos, size = next(draw for draw in draws if draw[0] == action_text)
assert size == 26
assert pill.x >= 1500 - mod.AETHER_LIST_METRICS.action_width
assert pill.x + pill.width <= 1500
assert pos.x >= pill.x + 11
assert pos.x + measure(None, text, size).x <= pill.x + pill.width - 11
def test_action_pill_preserves_explicit_width_and_rail_limit():
mod = _import_aethergrid()
row = mod.rl.Rectangle(0, 0, 1500, 128)
for requested_width in (120, 1000):
pill = mod.draw_selection_list_row(row, title="Asset", action_text="Download", action_pill=True,
action_pill_width=requested_width)
assert pill.width == min(requested_width, mod.AETHER_LIST_METRICS.action_width - 28)
class TestAethergridContracts(unittest.TestCase):
def test_aethergrid_module_imports_with_headless_stubs(self):
mod = _import_aethergrid()
@@ -0,0 +1,53 @@
from types import SimpleNamespace
import pytest
from openpilot.selfdrive.ui.layouts.settings.starpilot import simple_download_manager as downloads
from openpilot.system.ui.widgets import DialogResult
class FakeParams:
def __init__(self):
self.values = {}
def get(self, key, encoding=None):
return self.values.get(key, "")
def put(self, key, value):
self.values[key] = value
@pytest.mark.parametrize("result", [DialogResult.CANCEL, DialogResult.CONFIRM])
def test_asset_deletion_requires_confirmation_and_preserves_other_assets(tmp_path, monkeypatch, result):
target = tmp_path / "boot_logo_a.png"
other = tmp_path / "boot_logo_b.png"
target.write_bytes(b"target")
other.write_bytes(b"other")
params = FakeParams()
manager = downloads.SimpleDownloadManager(
"Boot Logo", "boot logo", tmp_path, "Asset", "Download", "Downloadable", params, FakeParams(), lambda *_: None,
)
manager._active_mode = manager.MODE_DELETE
manager._refresh_list()
dialogs = []
monkeypatch.setattr(downloads, "ConfirmDialog", lambda text, confirm_text, callback: SimpleNamespace(text=text, callback=callback))
monkeypatch.setattr(downloads.gui_app, "push_widget", dialogs.append)
manager._on_target("item:0")
assert len(dialogs) == 1
assert "Boot Logo A" in dialogs[0].text
assert target.exists() and other.exists()
assert not params.values
dialogs[0].callback(result)
assert other.read_bytes() == b"other"
if result == DialogResult.CONFIRM:
assert not target.exists()
assert manager._list_items == ["Boot Logo B"]
assert params.values["Downloadable"] == "boot_logo_a"
else:
assert target.read_bytes() == b"target"
assert manager._list_items == ["Boot Logo A", "Boot Logo B"]
assert not params.values
+16
View File
@@ -1128,3 +1128,19 @@ msgstr ""
#, python-format
msgid "Reboot Device"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1216,3 +1216,19 @@ msgstr "✓ مشترك"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 وضع Firehose 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1219,3 +1219,19 @@ msgstr "✓ ABONNIERT"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Firehose‑Modus 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr "Dann"
+16
View File
@@ -1205,3 +1205,19 @@ msgstr "✓ SUBSCRIBED"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Firehose Mode 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr "Auto: Off"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr "Auto: Weekly"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr "Auto: Monthly"
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr "Then"
+16
View File
@@ -1223,3 +1223,19 @@ msgstr "✓ SUSCRITO"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Modo Firehose 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr "Auto: No"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr "Auto: Semanal"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr "Auto: Mensual"
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr "Luego"
+16
View File
@@ -1228,3 +1228,19 @@ msgstr "✓ ABONNÉ"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Mode Firehose 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr "Auto : non"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr "Auto : hebdo"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr "Auto : mensuel"
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr "Puis"
+16
View File
@@ -1195,3 +1195,19 @@ msgstr "✓ 登録済み"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Firehoseモード 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1188,3 +1188,19 @@ msgstr "✓ 구독됨"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 파이어호스 모드 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1218,3 +1218,19 @@ msgstr "✓ ASSINADO"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Modo Firehose 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr "Depois"
+16
View File
@@ -1127,3 +1127,19 @@ msgstr ""
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1208,3 +1208,19 @@ msgstr "✓ ABONE"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Firehose Modu 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr "Sonra"
+16
View File
@@ -1256,3 +1256,19 @@ msgstr "✓ ПІДПИСАНО"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🌧️ Режим зливи 🌧️"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1172,3 +1172,19 @@ msgstr "✓ 已订阅"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Firehose 模式 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+16
View File
@@ -1171,3 +1171,19 @@ msgstr "✓ 已訂閱"
#, python-format
msgid "🔥 Firehose Mode 🔥"
msgstr "🔥 Firehose 模式 🔥"
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Off"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Weekly"
msgstr ""
#: selfdrive/ui/layouts/settings/starpilot/maps.py
msgid "Auto: Monthly"
msgstr ""
#: selfdrive/ui/onroad/starpilot/navigation_card.py
msgid "Then"
msgstr ""
+3 -3
View File
@@ -702,7 +702,7 @@ class DriveStatsDashboard:
max_distance = max(max_distance, 1.0)
slot_width = plot.width / max(len(self._data.daily_distance), 1)
bar_width = min(78.0, slot_width * 0.52)
value_headroom = 38.0
value_headroom = 48.0
bar_area_height = max(1.0, plot.height - value_headroom)
for index, day in enumerate(self._data.daily_distance):
center_x = plot.x + slot_width * (index + 0.5)
@@ -712,13 +712,13 @@ class DriveStatsDashboard:
if day.distance > 0.0:
value_text = _format_decimal(day.distance)
value_size = measure_text_cached(self._font_medium, value_text, 21)
value_size = measure_text_cached(self._font_medium, value_text, 26)
value_y = max(plot.y + 4, bar_rect.y - value_size.y - 8)
rl.draw_text_ex(
self._font_medium,
value_text,
rl.Vector2(center_x - value_size.x / 2, value_y),
21,
26,
0,
MUTED_COLOR,
)
+6 -6
View File
@@ -93,9 +93,9 @@ class PairingDialog(Widget):
title_font = gui_app.font(FontWeight.NORMAL)
left_width = int(content_rect.width * 0.5 - 15)
title_wrapped = wrap_text(title_font, title, 75, left_width)
rl.draw_text_ex(title_font, "\n".join(title_wrapped), rl.Vector2(content_rect.x, y), 75, 0.0, rl.BLACK)
y += len(title_wrapped) * 75 + 60
title_wrapped = "\n".join(wrap_text(title_font, title, 75, left_width))
rl.draw_text_ex(title_font, title_wrapped, rl.Vector2(content_rect.x, y), 75, 0.0, rl.BLACK)
y += measure_text_cached(title_font, title_wrapped, 75).y + 60
# Two columns: instructions and QR code
remaining_height = content_rect.height - (y - content_rect.y)
@@ -128,8 +128,8 @@ class PairingDialog(Widget):
text_x = rect.x + circle_radius * 2 + 40
text_width = rect.width - (circle_radius * 2 + 40)
wrapped = wrap_text(font, text, 47, int(text_width))
text_height = len(wrapped) * 47
wrapped = "\n".join(wrap_text(font, text, 47, int(text_width)))
text_height = measure_text_cached(font, wrapped, 47).y
circle_y = y + text_height // 2
# Circle and number
@@ -139,7 +139,7 @@ class PairingDialog(Widget):
rl.draw_text_ex(font, number, (int(circle_x - number_size.x // 2), int(circle_y - number_size.y // 2)), 30, 0, rl.WHITE)
# Text
rl.draw_text_ex(font, "\n".join(wrapped), rl.Vector2(text_x, y), 47, 0.0, rl.BLACK)
rl.draw_text_ex(font, wrapped, rl.Vector2(text_x, y), 47, 0.0, rl.BLACK)
y += text_height + 50
def _render_qr_code(self, rect: rl.Rectangle) -> None: