mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
a531afd02a
Expose pairing readiness and coordinate adapter hand-offs with Android Auto. Show app pairings separately, support individual removal, and improve pairing controls on both comma screens. Add Galaxy companion endpoints and web asset manifests, preserve the plain camera look, and fix headless speed-limit shapes.
132 lines
6.9 KiB
Python
132 lines
6.9 KiB
Python
"""Settings entry presentation with supplied state and no service construction."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pyray as rl
|
|
|
|
from openpilot.starpilot.ui import clip
|
|
|
|
from openpilot.starpilot.ui.home_geometry import outside_rounded_border
|
|
from openpilot.starpilot.ui.presentation import BitmapFonts, FontRole, Profile
|
|
from openpilot.starpilot.ui.settings_assets import SettingsAssets
|
|
from openpilot.starpilot.ui.settings_geometry import ACCENT, constellation, draw_constellation_nodes, draw_hud_background, draw_settings_header
|
|
from openpilot.starpilot.ui.settings_state import compact_menu, Destination, RAIL, TILES, SettingsState, tile_rects
|
|
|
|
ICON_COLOR = rl.Color(246, 242, 254, 255)
|
|
ICON_SCALE = 0.8 * (100 / 60) * 1.6
|
|
|
|
|
|
class SettingsView:
|
|
def __init__(self, fonts: BitmapFonts, asset_directory: Path):
|
|
self.fonts = fonts
|
|
self.assets = SettingsAssets(asset_directory)
|
|
|
|
def prepare(self) -> None:
|
|
"""Prepare owned vector targets outside any active render-texture mode."""
|
|
if self.fonts.profile == Profile.LARGE:
|
|
for _, _, icon in TILES:
|
|
self.assets.prepare_icon(icon, ICON_SCALE, ICON_COLOR)
|
|
|
|
def render(self, state: SettingsState) -> None:
|
|
if self.fonts.profile == Profile.LARGE:
|
|
self._large(state)
|
|
else:
|
|
self._compact(state)
|
|
|
|
def _large(self, state: SettingsState):
|
|
draw_settings_header(self.fonts, state.sidebar_expanded, "StarPilot", back=True)
|
|
for (_, title, icon), bounds in zip(TILES, tile_rects(state), strict=True):
|
|
rect = rl.Rectangle(*bounds)
|
|
draw_hud_background(rect, ACCENT)
|
|
nodes, edges = constellation(title, rect)
|
|
draw_constellation_nodes(nodes, edges, rect, ACCENT, 1)
|
|
text_scale = max(0.82, min(1.12, min(rect.width / 360, rect.height / 205)))
|
|
size = max(44, round(50 * text_scale))
|
|
icon_height = 128.0
|
|
top = rect.y + max(0, (rect.height - icon_height - 8 - size) / 2)
|
|
self.assets.icon(icon, rect.x + (rect.width - icon_height) / 2, top, ICON_SCALE, ICON_COLOR)
|
|
self._fit_title(title, rect.x + 16, top + icon_height + 8, rect.width - 32, size)
|
|
self.render_rail(state)
|
|
|
|
def _fit_title(self, text, x, y, width, size):
|
|
original_size = size
|
|
measured = self.fonts.measure(text, FontRole.MEDIUM, size)
|
|
if measured.width > width:
|
|
high, low = max(16, round(size * width / measured.width)), 16
|
|
size = high
|
|
while low < high:
|
|
middle = (low + high + 1) // 2
|
|
if self.fonts.measure(text, FontRole.MEDIUM, middle).width <= width:
|
|
low = size = middle
|
|
else:
|
|
high = middle - 1
|
|
measured = self.fonts.measure(text, FontRole.MEDIUM, size)
|
|
self.fonts.draw(text, FontRole.MEDIUM, size, round(x + (width - measured.width) / 2), round(y + (original_size - size) / 2))
|
|
|
|
def render_rail(self, state: SettingsState, selected: Destination = Destination.STAR) -> None:
|
|
"""Draw the shared large navigation rail for a supplied selected panel."""
|
|
width = 500 if state.sidebar_expanded else 0
|
|
rl.draw_rectangle_rec(rl.Rectangle(0, 0, width, 1080), rl.BLACK)
|
|
rl.draw_rectangle_rec(rl.Rectangle(0, 0, 2, 1080), rl.Color(139, 92, 246, 55))
|
|
rl.draw_rectangle_rounded(rl.Rectangle(-40, 501, 90, 160), 0.5, 30, rl.Color(139, 92, 246, 30))
|
|
tab = rl.Rectangle(-30, 511, 70, 140)
|
|
rl.draw_rectangle_rounded(tab, 0.5, 30, rl.Color(139, 92, 246, 60))
|
|
outside_rounded_border(tab, 0.5, 30, 2, rl.Color(139, 92, 246, 160))
|
|
sign = 1 if state.sidebar_expanded else -1
|
|
points = (rl.Vector2(20 + sign * 12, 557), rl.Vector2(20 - sign * 12, 581), rl.Vector2(20 + sign * 12, 605))
|
|
for color, thickness in ((rl.Color(255, 255, 255, 35), 7), (rl.WHITE, 2.8)):
|
|
rl.draw_line_ex(points[0], points[1], thickness, color)
|
|
rl.draw_line_ex(points[1], points[2], thickness, color)
|
|
if not state.sidebar_expanded:
|
|
return
|
|
rl.draw_rectangle_rounded(rl.Rectangle(150, 60, 200, 200), 1, 20, rl.Color(41, 41, 41, 255))
|
|
texture = self.assets.image("icons/backspace.png", 70, 70)
|
|
rl.draw_texture_pro(texture, rl.Rectangle(0, 0, 70, 70), rl.Rectangle(215, 125, 70, 70), rl.Vector2(0, 0), 0, rl.WHITE)
|
|
for index, (destination, text) in enumerate(RAIL):
|
|
size = self.fonts.measure(text, FontRole.MEDIUM, 65)
|
|
self.fonts.draw(text, FontRole.MEDIUM, 65, round(400 - size.width), round(300 + index * 110 + (110 - size.height) / 2),
|
|
rl.WHITE if destination == selected else rl.Color(128, 128, 128, 255))
|
|
|
|
def _compact(self, state: SettingsState):
|
|
y = state.compact_y
|
|
viewport = rl.Rectangle(0, y, 536, 240)
|
|
rl.draw_rectangle_rec(rl.Rectangle(0, 0, 536, 240), rl.Color(0, 0, 0, int(200 * max(0, min(1, 1 - y / 240)))))
|
|
rl.draw_rectangle_rec(rl.Rectangle(0, y, 536, 260), rl.BLACK)
|
|
background = self.assets.image("icons_mici/buttons/button_rectangle.png", 402, 180)
|
|
clip.begin_scissor_mode(0, int(y), 536, 240)
|
|
try:
|
|
menu = compact_menu(state)
|
|
for index in reversed(range(len(menu))):
|
|
destination, text, filename, width, height = menu[index]
|
|
x, top = 20 + index * 422 + state.compact_scroll_x, y + 30
|
|
if x >= 536 or x + 402 <= 0:
|
|
continue
|
|
rl.draw_texture_ex(background, rl.Vector2(x, top), 0, 1, rl.WHITE)
|
|
font_size = 40 if destination == Destination.FORCE_DRIVE else 42 if destination == Destination.DRIVING_MODEL else (
|
|
48 if destination in (Destination.GALAXY, Destination.PAIR) else 64)
|
|
if destination == Destination.DRIVING_CONTROLS:
|
|
font_size = 56
|
|
text = text.replace(" ", "\n", 1)
|
|
measured = self.fonts.measure(text, FontRole.BOLD, font_size)
|
|
self.fonts.draw(text, FontRole.BOLD, font_size, x + 40, top + 180 - 23 - measured.height,
|
|
rl.Color(255, 255, 255, 229))
|
|
if filename is not None:
|
|
icon = self.assets.image(filename, width, height)
|
|
width, height = icon.width, icon.height
|
|
rl.draw_texture_pro(icon, rl.Rectangle(0, 0, width, height),
|
|
rl.Rectangle(x + 402 - 30 - width / 2, top + 30 + height / 2, width, height),
|
|
rl.Vector2(width / 2, height / 2), 0, rl.Color(255, 255, 255, 229))
|
|
finally:
|
|
clip.end_scissor_mode()
|
|
rl.draw_rectangle_gradient_h(0, int(y), 20, 240, rl.Color(0, 0, 0, 204), rl.BLANK)
|
|
rl.draw_rectangle_gradient_h(516, int(y), 20, 240, rl.BLANK, rl.Color(0, 0, 0, 204))
|
|
indicator = self.assets.image("icons_mici/settings/horizontal_scroll_indicator.png", 96, 48)
|
|
rl.draw_texture_pro(indicator, rl.Rectangle(0, 0, 96, 48), rl.Rectangle(0, max(viewport.y, 0) + 216, 100, 48),
|
|
rl.Vector2(0, 0), 0, rl.Color(255, 255, 255, 114))
|
|
bar = rl.Rectangle(165.5, state.nav_bar_y, 205, 8)
|
|
rl.draw_rectangle_rounded(bar, 1, 6, rl.Color(255, 255, 255, int(255 * 0.9 * state.nav_bar_alpha)))
|
|
outside_rounded_border(bar, 1, 6, 2, rl.Color(0, 0, 0, int(255 * 0.3 * state.nav_bar_alpha)))
|
|
|
|
def close(self) -> None:
|
|
self.assets.close()
|