mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-05 05:44:03 +08:00
polish
This commit is contained in:
@@ -20,6 +20,7 @@ from openpilot.system.hardware import HARDWARE, PC, TICI
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.ui.lib.application import FontWeight, gui_app
|
||||
from openpilot.system.ui.lib.multilang import multilang, tr, tr_noop
|
||||
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, alert_dialog
|
||||
from openpilot.system.ui.widgets.html_render import HtmlModal
|
||||
@@ -80,7 +81,7 @@ class GalaxyQRDialog(Widget):
|
||||
|
||||
def _render_centered_text(self, rect: rl.Rectangle, text: str, y: float, font_size: int, color: rl.Color, font_weight: FontWeight = FontWeight.NORMAL) -> None:
|
||||
font = gui_app.font(font_weight)
|
||||
size = rl.measure_text_ex(font, text, font_size, 0)
|
||||
size = measure_text_cached(font, text, font_size)
|
||||
rl.draw_text_ex(font, text, rl.Vector2(rect.x + (rect.width - size.x) / 2, y), font_size, 0, color)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
|
||||
@@ -9,6 +9,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, M
|
||||
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.lib.wrap_text import wrap_text as wrap_body_text
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.label import gui_label
|
||||
|
||||
@@ -214,7 +215,7 @@ def draw_text_fit_common(
|
||||
render_width = measure_text_cached(font, text, actual_font_size, spacing=spacing).x
|
||||
else:
|
||||
render_width = size.x
|
||||
nudge_y = (font_size - actual_font_size) / 2
|
||||
nudge_y = (font_size - actual_font_size) * FONT_SCALE / 2
|
||||
draw_x = pos.x
|
||||
if align_center:
|
||||
draw_x = pos.x + (max_width - render_width) / 2
|
||||
@@ -763,7 +764,7 @@ class PanelManagerView(AetherInteractiveMixin, Widget):
|
||||
PAGE_COMMIT_RATIO = 0.20
|
||||
PAGE_ANIM_DURATION = 0.28
|
||||
PAGE_SNAP_DURATION = 0.20
|
||||
PAGE_INDICATOR_HEIGHT = 44
|
||||
PAGE_INDICATOR_HEIGHT = 56
|
||||
|
||||
|
||||
|
||||
@@ -2032,12 +2033,12 @@ def draw_section_header(
|
||||
):
|
||||
if title:
|
||||
trailing_reserved = min(320.0, rect.width * 0.38) if trailing_text else 0.0
|
||||
title_rect = rl.Rectangle(rect.x, rect.y + (rect.height - title_size) / 2, max(1.0, rect.width - trailing_reserved), title_size + 4)
|
||||
title_rect = rl.Rectangle(rect.x, rect.y, max(1.0, rect.width - trailing_reserved), rect.height)
|
||||
alignment = rl.GuiTextAlignment.TEXT_ALIGN_CENTER if align_center else rl.GuiTextAlignment.TEXT_ALIGN_LEFT
|
||||
gui_label(title_rect, title, title_size, title_color or style.subtitle_color, FontWeight.SEMI_BOLD, alignment=alignment)
|
||||
|
||||
if trailing_text:
|
||||
trailing_rect = rl.Rectangle(rect.x, rect.y + (rect.height - trailing_size) / 2, rect.width, trailing_size + 4)
|
||||
trailing_rect = rect
|
||||
gui_label(
|
||||
trailing_rect,
|
||||
trailing_text,
|
||||
@@ -2084,14 +2085,28 @@ def draw_empty_state_card(
|
||||
FontWeight.MEDIUM,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
)
|
||||
gui_label(
|
||||
rl.Rectangle(card_rect.x + inset_x, body_y, max(1.0, card_rect.width - inset_x * 2), resolved_body_h),
|
||||
body,
|
||||
body_size,
|
||||
style.subtitle_color,
|
||||
FontWeight.NORMAL,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
)
|
||||
resolved_body_h = min(resolved_body_h, max(0.0, card_rect.y + card_rect.height - body_y))
|
||||
font = gui_app.font(FontWeight.NORMAL)
|
||||
line_height = body_size * FONT_SCALE
|
||||
line_gap = 4.0
|
||||
max_lines = int((resolved_body_h + line_gap) // (line_height + line_gap))
|
||||
if not body or max_lines < 1:
|
||||
return
|
||||
body_width = max(1.0, card_rect.width - inset_x * 2)
|
||||
if "\n" not in body and measure_text_cached(font, body, body_size).x <= body_width:
|
||||
lines = [body]
|
||||
else:
|
||||
lines = wrap_body_text(font, body, body_size, int(body_width))
|
||||
if len(lines) > max_lines:
|
||||
lines = lines[:max_lines - 1] + [truncate_text_ellipsis(font, " ".join(lines[max_lines - 1:]), body_width, body_size)]
|
||||
text_y = body_y + (resolved_body_h - len(lines) * line_height - (len(lines) - 1) * line_gap) / 2
|
||||
for line in lines:
|
||||
gui_label(
|
||||
rl.Rectangle(card_rect.x + inset_x, text_y, body_width, line_height),
|
||||
line, body_size, style.subtitle_color, FontWeight.NORMAL,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
)
|
||||
text_y += line_height + line_gap
|
||||
|
||||
|
||||
def draw_list_group_shell(
|
||||
@@ -2573,7 +2588,7 @@ class AetherInlineRangeControl(Widget):
|
||||
draw_text_fit_common(
|
||||
self._font,
|
||||
label,
|
||||
rl.Vector2(rect.x + 10, rect.y + (rect.height - 22) / 2),
|
||||
rl.Vector2(rect.x + 10, rect.y + (rect.height - 22 * FONT_SCALE) / 2),
|
||||
max(1.0, rect.width - 20),
|
||||
22, # font_size in draw_button
|
||||
align_center=True,
|
||||
@@ -3114,15 +3129,14 @@ class AetherButton(Widget):
|
||||
accent = self._accent_color or AetherListColors.PRIMARY
|
||||
bg = accent if enabled else rl.Color(accent.r, accent.g, accent.b, 80)
|
||||
border = with_alpha(accent, 190 if enabled else 70)
|
||||
if hovered:
|
||||
bg = rl.Color(min(bg.r + 10, 255), min(bg.g + 10, 255), min(bg.b + 10, 255), bg.a)
|
||||
if pressed:
|
||||
bg = rl.Color(max(bg.r - 8, 0), max(bg.g - 8, 0), max(bg.b - 8, 0), bg.a)
|
||||
else:
|
||||
bg = rl.Color(255, 255, 255, 10 if enabled else 5)
|
||||
bg = rl.Color(255, 255, 255, (20 if pressed else 14 if hovered else 10) if enabled else 5)
|
||||
border = rl.Color(255, 255, 255, 22 if enabled else 10)
|
||||
|
||||
if hovered:
|
||||
bg = rl.Color(min(bg.r + 10, 255), min(bg.g + 10, 255), min(bg.b + 10, 255), bg.a)
|
||||
if pressed:
|
||||
bg = rl.Color(max(bg.r - 8, 0), max(bg.g - 8, 0), max(bg.b - 8, 0), bg.a)
|
||||
|
||||
rl.draw_rectangle_rounded(rect, 0.18, 12, bg)
|
||||
rl.draw_rectangle_rounded_lines_ex(rect, 0.18, 12, 1, border)
|
||||
rl.draw_rectangle_rec(rl.Rectangle(rect.x, rect.y, rect.width, 1), with_alpha(AetherListColors.HEADER, 18 if enabled else 8))
|
||||
@@ -3159,7 +3173,7 @@ class AetherChip:
|
||||
draw_text_fit_common(
|
||||
gui_app.font(FontWeight.MEDIUM),
|
||||
self.text,
|
||||
rl.Vector2(rect.x + 12, rect.y + (rect.height - self._font_size) / 2),
|
||||
rl.Vector2(rect.x + 12, rect.y + (rect.height - self._font_size * FONT_SCALE) / 2),
|
||||
max(1.0, rect.width - 24),
|
||||
self._font_size,
|
||||
align_center=True,
|
||||
@@ -5605,4 +5619,3 @@ class TileGrid(Widget):
|
||||
tile.set_parent_rect(parent_rect)
|
||||
tile.render(snap_rect(rl.Rectangle(row_x + c * (row_tile_w + self._gap), rect.y + y_offset + r * (tile_h + self._gap), row_tile_w, tile_h)))
|
||||
tile_idx += 1
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ class DrivingModelManagerView(AetherInteractiveMixin, Widget):
|
||||
enabled=lambda: self._controller.primary_header_button_state()[1],
|
||||
emphasized=True,
|
||||
font_size=SETTINGS_ROW_TITLE_FONT_SIZE,
|
||||
accent_color=rl.Color(139, 92, 246, 92),
|
||||
accent_color=with_alpha(AetherListColors.PRIMARY, 92),
|
||||
)
|
||||
)
|
||||
self._secondary_header_button = self._child(
|
||||
@@ -478,7 +478,7 @@ class DrivingModelManagerView(AetherInteractiveMixin, Widget):
|
||||
body_size=26,
|
||||
body_inset_x=48,
|
||||
title_top_padding=32,
|
||||
body_height=60,
|
||||
body_height=80,
|
||||
style=PANEL_STYLE,
|
||||
)
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ class StarPilotLateralLayout(_SettingsPage):
|
||||
),
|
||||
SettingRow(
|
||||
"LaneChangeCloseGapSeconds", "value", tr_noop("Temporary Follow Distance"),
|
||||
subtitle=tr_noop("Follow distance to hold while changing lanes. Only applied when shorter than your normal gap."),
|
||||
subtitle=tr_noop("Gap to use during lane changes, only when shorter than your normal gap."),
|
||||
get_value=self._get_lane_change_close_gap_display,
|
||||
on_click=lambda: self._show_slider("LaneChangeCloseGapSeconds", 0.25, 1.0, step=0.05, unit="s", value_type="float"),
|
||||
visible=close_gap_on,
|
||||
|
||||
@@ -520,7 +520,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
|
||||
on_click=lambda: self._show_slider("LeadDetectionThreshold", 25, 50, unit="%"),
|
||||
visible=self._longitudinal_enabled),
|
||||
SettingRow("NavLongitudinalAllowed", "toggle", tr_noop("Use Route Speed Control"),
|
||||
subtitle=tr_noop("Allow an active navigation route to reduce cruise speed for upcoming turns, ramps, and roundabouts."),
|
||||
subtitle=tr_noop("Use the active route to reduce cruise speed for upcoming turns, ramps, and roundabouts."),
|
||||
get_state=lambda: self._params.get_bool("NavLongitudinalAllowed"),
|
||||
set_state=lambda s: self._params.put_bool("NavLongitudinalAllowed", s),
|
||||
visible=self._longitudinal_enabled),
|
||||
@@ -654,7 +654,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
|
||||
# ── 4. Vision Speed Limits Rows ──
|
||||
self._vision_speed_limit_rows = [
|
||||
SettingRow("VisionSpeedLimit", "toggle", tr_noop("Vision Detection"),
|
||||
subtitle=tr_noop("Use the road camera to detect and display speed-limit signs, with optional use by Speed Limit Controller."),
|
||||
subtitle=tr_noop("Display camera-detected speed-limit signs; optionally use them with Speed Limit Controller."),
|
||||
get_state=lambda: self._params.get_bool("VisionSpeedLimitDetection"),
|
||||
set_state=lambda s: self._params.put_bool("VisionSpeedLimitDetection", s)),
|
||||
]
|
||||
@@ -729,7 +729,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
|
||||
current_value=max(1, self._params.get_float("CustomCruiseLong"))),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and not starpilot_state.car_state.isToyota),
|
||||
SettingRow("ReverseCruise", "toggle", tr_noop("Reverse Cruise Increase"),
|
||||
subtitle=tr_noop("Swap Toyota/Lexus cruise increments: short press changes the dash set speed by 5; hold changes it by 1."),
|
||||
subtitle=tr_noop("Swap Toyota/Lexus steps: short press changes dash set speed by 5; hold changes it by 1."),
|
||||
get_state=lambda: self._params.get_bool("ReverseCruise"),
|
||||
set_state=lambda s: self._params.put_bool("ReverseCruise", s),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and starpilot_state.car_state.isToyota),
|
||||
@@ -744,7 +744,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
|
||||
on_click=lambda: self._show_slider("ForceStopDistanceOffset", -20, 20, unit=" ft"),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and self._params.get_bool("ForceStops")),
|
||||
SettingRow("RadarTakeoffs", "toggle", tr_noop("Radar for Takeoffs"),
|
||||
subtitle=tr_noop("Turns on/off using radar data to track leads at standstill, making following/takeoffs more responsive once leads move."),
|
||||
subtitle=tr_noop("Use radar to track leads at standstill and respond faster when they move."),
|
||||
get_state=lambda: self._params.get_bool("RadarTakeoffs"),
|
||||
set_state=lambda s: self._params.put_bool("RadarTakeoffs", s),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and starpilot_state.car_state.hasRadar),
|
||||
|
||||
@@ -94,7 +94,7 @@ STATUS_CARD_HEIGHT = 232.0
|
||||
SEGMENTED_CONTROL_HEIGHT = 68.0
|
||||
BROWSER_SECTION_HEADER_HEIGHT = 56.0
|
||||
BROWSER_REGION_ROW_HEIGHT = 104.0
|
||||
BROWSER_EMPTY_STATE_HEIGHT = 128.0
|
||||
BROWSER_EMPTY_STATE_HEIGHT = 208.0
|
||||
BROWSER_INSET = 18.0
|
||||
|
||||
HEADER_GAP = 16.0
|
||||
|
||||
@@ -229,7 +229,7 @@ NAV_SUMMARY_HEIGHT = 124.0
|
||||
NAV_ACTION_HEIGHT = 78.0
|
||||
NAV_SECTION_HEIGHT = 72.0
|
||||
NAV_ROW_HEIGHT = 124.0
|
||||
NAV_EMPTY_HEIGHT = 132.0
|
||||
NAV_EMPTY_HEIGHT = 208.0
|
||||
NAV_ACTION_COLUMNS = 3
|
||||
NAV_ACTION_GAP = 12.0
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
|
||||
point_hits,
|
||||
draw_text_fit_common,
|
||||
wrap_text,
|
||||
with_alpha,
|
||||
SECTION_GAP,
|
||||
ROW_HEIGHT,
|
||||
SPACING,
|
||||
@@ -466,8 +467,8 @@ class SystemSettingsManagerView(PanelManagerView):
|
||||
hovered, pressed = self._interactive_state("static:first_aid", btn_rect, pad_y=6)
|
||||
|
||||
if pressed:
|
||||
fill = rl.Color(139, 92, 246, 8)
|
||||
border = rl.Color(139, 92, 246, 28)
|
||||
fill = with_alpha(AetherListColors.PRIMARY, 8)
|
||||
border = with_alpha(AetherListColors.PRIMARY, 28)
|
||||
elif hovered:
|
||||
fill = rl.Color(255, 255, 255, 4)
|
||||
border = rl.Color(255, 255, 255, 10)
|
||||
@@ -481,7 +482,7 @@ class SystemSettingsManagerView(PanelManagerView):
|
||||
icon_x = btn_rect.x + (btn_rect.width - 60.0 * s) / 2.0
|
||||
icon_y = btn_rect.y + (btn_rect.height - 60.0 * s) / 2.0
|
||||
if pressed:
|
||||
icon_color = rl.Color(139, 92, 246, 190)
|
||||
icon_color = with_alpha(AetherListColors.PRIMARY, 190)
|
||||
elif hovered:
|
||||
icon_color = rl.Color(160, 170, 185, 170)
|
||||
else:
|
||||
|
||||
@@ -22,6 +22,9 @@ from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
|
||||
PanelManagerView,
|
||||
RowToggleTile,
|
||||
SPACING,
|
||||
SETTINGS_ROW_TITLE_FONT_SIZE,
|
||||
SETTINGS_ROW_SUBTITLE_FONT_SIZE,
|
||||
SETTINGS_ROW_VALUE_FONT_SIZE,
|
||||
SettingRow,
|
||||
TileGrid,
|
||||
TOGGLE_MIN_HEIGHT,
|
||||
@@ -199,9 +202,9 @@ class VehicleSettingsManagerView(PanelManagerView):
|
||||
subtitle_size = 26
|
||||
value_size = 28
|
||||
else:
|
||||
title_size = 40
|
||||
subtitle_size = 28
|
||||
value_size = 34
|
||||
title_size = SETTINGS_ROW_TITLE_FONT_SIZE
|
||||
subtitle_size = SETTINGS_ROW_SUBTITLE_FONT_SIZE
|
||||
value_size = SETTINGS_ROW_VALUE_FONT_SIZE
|
||||
|
||||
if row.type == "value" or row.id.startswith("combo:"):
|
||||
value_text = row.get_value() if row.get_value else ""
|
||||
|
||||
@@ -9,7 +9,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 FontWeight, gui_app
|
||||
from openpilot.system.ui.lib.application import FONT_SCALE, FontWeight, gui_app
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
@@ -341,17 +341,20 @@ class NavigationCardRenderer(Widget):
|
||||
title_lines, title_font_size = self._fit_title(self._primary_text, text_area_width, preferred_font_size, minimum_font_size)
|
||||
|
||||
secondary_font_size = 26
|
||||
text_block_h = len(title_lines) * title_font_size
|
||||
title_line_height = title_font_size * FONT_SCALE
|
||||
secondary_height = secondary_font_size * FONT_SCALE
|
||||
title_lines = title_lines[:2]
|
||||
text_block_h = len(title_lines) * title_line_height
|
||||
if self._secondary_text:
|
||||
text_block_h += 8 + secondary_font_size
|
||||
text_block_h += 8 + secondary_height
|
||||
text_block_y = container_y + (card_height - text_block_h) // 2
|
||||
|
||||
for index, line in enumerate(title_lines[:2]):
|
||||
rl.draw_text_ex(self._font_bold, line, rl.Vector2(text_x, text_block_y + index * title_font_size), title_font_size, 0, rl.WHITE)
|
||||
for index, line in enumerate(title_lines):
|
||||
rl.draw_text_ex(self._font_bold, line, rl.Vector2(text_x, text_block_y + index * title_line_height), title_font_size, 0, rl.WHITE)
|
||||
|
||||
if self._secondary_text:
|
||||
secondary_text = self._truncate_text(self._secondary_text, text_area_width, secondary_font_size)
|
||||
secondary_y = text_block_y + len(title_lines) * title_font_size + 8
|
||||
secondary_y = text_block_y + len(title_lines) * title_line_height + 8
|
||||
rl.draw_text_ex(
|
||||
self._font_medium,
|
||||
secondary_text,
|
||||
|
||||
@@ -71,6 +71,7 @@ def _install_aethergrid_stubs():
|
||||
app_mod.MousePos = type("MousePos", (), {})
|
||||
app_mod.MouseEvent = type("MouseEvent", (), {})
|
||||
app_mod.FONT_SCALE = 1.0
|
||||
app_mod.font_fallback = lambda font: font
|
||||
app_mod.gui_app = types.SimpleNamespace(
|
||||
width=1920,
|
||||
height=1080,
|
||||
@@ -220,6 +221,34 @@ class RenderSpy:
|
||||
self.parent_rect = rect
|
||||
|
||||
|
||||
@pytest.mark.parametrize("height, body_size, body_height", [(208, 26, None), (208, 24, None), (400, 26, 80), (128, 26, None)])
|
||||
@pytest.mark.parametrize("body", ["A long explanation " * 30, "unbroken" * 50, "First line\nSecond line\nThird line"])
|
||||
def test_empty_state_body_wraps_within_card(height, body_size, body_height, body):
|
||||
mod = _import_aethergrid()
|
||||
wrapper = importlib.import_module("openpilot.system.ui.lib.wrap_text")
|
||||
mod.FONT_SCALE = 1.242
|
||||
font = types.SimpleNamespace(texture=types.SimpleNamespace(id=1))
|
||||
mod.gui_app.font = lambda *_: font
|
||||
labels = []
|
||||
|
||||
def measure(_font, text, size, spacing=0):
|
||||
return types.SimpleNamespace(x=len(text) * size * mod.FONT_SCALE / 2, y=size * mod.FONT_SCALE)
|
||||
|
||||
with patch.object(mod, "measure_text_cached", measure), patch.object(wrapper, "measure_text_cached", measure), \
|
||||
patch.object(mod, "gui_label", lambda rect, text, *a, **kw: labels.append((rect, text))):
|
||||
mod.draw_empty_state_card(mod.rl.Rectangle(0, 0, 500, height), "Title", body,
|
||||
title_size=30, body_size=body_size, body_height=body_height)
|
||||
|
||||
body_labels = labels[1:]
|
||||
assert body_labels
|
||||
for rect, text in body_labels:
|
||||
assert rect.y >= labels[0][0].y + labels[0][0].height
|
||||
assert rect.y + rect.height <= height
|
||||
assert measure(font, text, body_size).x <= rect.width
|
||||
if body.startswith(("A long", "unbroken")):
|
||||
assert body_labels[-1][1].endswith("...")
|
||||
|
||||
|
||||
class TestAethergridContracts(unittest.TestCase):
|
||||
def test_aethergrid_module_imports_with_headless_stubs(self):
|
||||
mod = _import_aethergrid()
|
||||
|
||||
Reference in New Issue
Block a user