BigUI WIP: Cleanup/uniformity

This commit is contained in:
firestarsdog
2026-05-07 19:57:18 -04:00
parent ea64cc4de3
commit 1fb31b2e12
4 changed files with 153 additions and 171 deletions
@@ -108,10 +108,7 @@ class DrivingModelManagerView(Widget):
self._scroll_offset = 0.0
self._pressed_target: str | None = None
self._can_click = True
self._row_rects: dict[str, rl.Rectangle] = {}
self._action_rects: dict[str, rl.Rectangle] = {}
self._utility_rects: dict[str, rl.Rectangle] = {}
self._menu_sub_rects: dict[str, rl.Rectangle] = {}
self._interactive_rects: dict[str, rl.Rectangle] = {}
self._confirm_key: str | None = None
self._confirm_until = 0.0
self._transition_starts: dict[str, tuple[float, float]] = {}
@@ -202,36 +199,26 @@ class DrivingModelManagerView(Widget):
self._can_click = True
def _handle_mouse_event(self, mouse_event: MouseEvent):
del mouse_event
if not self._scroll_panel.is_touch_valid():
self._can_click = False
return
if self._pressed_target is not None and self._target_at(mouse_event.pos) != self._pressed_target:
self._pressed_target = None
def _handle_mouse_release(self, mouse_pos: MousePos):
target = self._target_at(mouse_pos)
target = self._target_at(mouse_pos) if self._scroll_panel.is_touch_valid() else None
if self._pressed_target is not None and self._pressed_target == target and self._can_click:
self._activate_target(target)
self._pressed_target = None
self._can_click = True
def _target_at(self, mouse_pos: MousePos) -> str | None:
for sub_key, rect in self._menu_sub_rects.items():
if _point_hits(mouse_pos, rect, self._scroll_rect, pad_x=6, pad_y=6):
return f"menu:{sub_key}"
for key, rect in self._action_rects.items():
visible_rect = rl.get_collision_rec(rect, self._scroll_rect)
if visible_rect.width > 0 and visible_rect.height > 0 and rl.check_collision_point_rec(mouse_pos, visible_rect):
return f"action:{key}"
for name, rect in self._utility_rects.items():
visible_rect = rl.get_collision_rec(rect, self._scroll_rect)
if visible_rect.width > 0 and visible_rect.height > 0 and rl.check_collision_point_rec(mouse_pos, visible_rect):
return f"utility:{name}"
for key, rect in self._row_rects.items():
visible_rect = rl.get_collision_rec(rect, self._scroll_rect)
if visible_rect.width > 0 and visible_rect.height > 0 and rl.check_collision_point_rec(mouse_pos, visible_rect):
return f"row:{key}"
for prefix in ("menu:", "action:", "utility:", "row:"):
for target_id, rect in self._interactive_rects.items():
if target_id.startswith(prefix):
pad_y = 6 if prefix == "menu:" else 0
if _point_hits(mouse_pos, rect, self._scroll_rect, pad_x=6, pad_y=pad_y):
return target_id
return None
def _activate_target(self, target: str | None):
@@ -291,10 +278,7 @@ class DrivingModelManagerView(Widget):
def _render(self, rect: rl.Rectangle):
self.set_rect(rect)
self._row_rects.clear()
self._action_rects.clear()
self._utility_rects.clear()
self._menu_sub_rects.clear()
self._interactive_rects.clear()
frame, scroll_rect, content_width = init_list_panel(rect, PANEL_STYLE)
self._shell_rect = frame.shell
@@ -389,7 +373,7 @@ class DrivingModelManagerView(Widget):
def _draw_empty_state(self, rect: rl.Rectangle):
draw_empty_state_card(
rl.Rectangle(rect.x, rect.y, rect.width - AETHER_LIST_METRICS.content_right_gutter, rect.height),
rl.Rectangle(rect.x, rect.y, rect.width, rect.height),
self._controller.empty_state_title(),
self._controller.empty_state_body(),
title_size=32,
@@ -401,17 +385,20 @@ class DrivingModelManagerView(Widget):
)
def _draw_model_section(self, x: float, y: float, width: float, title: str, entries: list[ModelCatalogEntry]) -> float:
draw_section_header(rl.Rectangle(x, y, width - AETHER_LIST_METRICS.content_right_gutter, SECTION_HEADER_HEIGHT), title, style=PANEL_STYLE)
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), title, style=PANEL_STYLE)
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
group_rect = rl.Rectangle(x, y, width, len(entries) * ROW_HEIGHT)
draw_list_group_shell(group_rect, style=PANEL_STYLE)
for index, entry in enumerate(entries):
row_rect = rl.Rectangle(x, y + index * ROW_HEIGHT, width - AETHER_LIST_METRICS.content_right_gutter, ROW_HEIGHT)
row_rect = rl.Rectangle(x, y + index * ROW_HEIGHT, width, ROW_HEIGHT)
self._draw_model_row(row_rect, entry, is_last=index == len(entries) - 1)
return y + len(entries) * ROW_HEIGHT
def _draw_model_row(self, rect: rl.Rectangle, entry: ModelCatalogEntry, is_last: bool):
mouse_pos = gui_app.last_mouse_event.pos
row_hovered = rl.check_collision_point_rec(mouse_pos, rect)
row_hovered = bool(_point_hits(mouse_pos, rect, self._scroll_rect, pad_x=6, pad_y=0))
target_key = f"row:{entry.key}"
pressed = self._pressed_target == target_key
current = self._controller.is_current_model(entry.key)
@@ -446,7 +433,7 @@ class DrivingModelManagerView(Widget):
info_rect = rl.Rectangle(draw_rect.x + 24, draw_rect.y + 18, draw_rect.width - ACTION_WIDTH - 42, draw_rect.height - 36)
row_touchable = entry.installed and not self._controller._params.get_bool("ModelRandomizer")
if row_touchable:
self._row_rects[entry.key] = draw_rect
self._interactive_rects[f"row:{entry.key}"] = draw_rect
self._draw_model_info(info_rect, entry, current)
@@ -456,10 +443,10 @@ class DrivingModelManagerView(Widget):
elif not removable:
self._draw_protected_action(action_rect)
else:
self._action_rects[entry.key] = action_rect
self._interactive_rects[f"action:{entry.key}"] = action_rect
self._draw_menu_action(action_rect, is_menu_open, entry)
else:
self._action_rects[entry.key] = action_rect
self._interactive_rects[f"action:{entry.key}"] = action_rect
if downloading:
self._draw_downloading_action(action_rect, self._controller.download_progress_text())
else:
@@ -548,8 +535,8 @@ class DrivingModelManagerView(Widget):
delete_rect = rl.Rectangle(rect.x + 10, start_y, rect.width - 20, btn_h)
fav_rect = rl.Rectangle(rect.x + 10, start_y + btn_h + gap, rect.width - 20, btn_h)
self._menu_sub_rects[f"{entry.key}:delete"] = delete_rect
self._menu_sub_rects[f"{entry.key}:favorite"] = fav_rect
self._interactive_rects[f"menu:{entry.key}:delete"] = delete_rect
self._interactive_rects[f"menu:{entry.key}:favorite"] = fav_rect
# Delete button
draw_action_pill(delete_rect, tr("Delete"), AetherListColors.DANGER_SOFT, rl.Color(AetherListColors.DANGER.r, AetherListColors.DANGER.g, AetherListColors.DANGER.b, min(AetherListColors.DANGER.a, 70)), AetherListColors.DANGER)
@@ -571,7 +558,7 @@ class DrivingModelManagerView(Widget):
AetherChip(tr("Protected"), rl.Color(255, 255, 255, 10), AetherListColors.MUTED, AetherListColors.SUBTEXT, font_size=18).render(chip_rect)
def _draw_utility_section(self, x: float, y: float, width: float, rows: list[dict]):
content_w = width - AETHER_LIST_METRICS.content_right_gutter
content_w = width
draw_section_header(rl.Rectangle(x, y, content_w, SECTION_HEADER_HEIGHT), tr("Automation and Tuning"), style=PANEL_STYLE)
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
@@ -584,9 +571,9 @@ class DrivingModelManagerView(Widget):
def _draw_utility_row(self, rect: rl.Rectangle, row: dict, is_last: bool):
mouse_pos = gui_app.last_mouse_event.pos
hovered = rl.check_collision_point_rec(mouse_pos, rect)
hovered = bool(_point_hits(mouse_pos, rect, self._scroll_rect, pad_x=6, pad_y=0))
pressed = self._pressed_target == f"utility:{row['id']}"
self._utility_rects[row["id"]] = rect
self._interactive_rects[f"utility:{row['id']}"] = rect
draw_settings_list_row(
rect,
title=row["title"],
+13 -13
View File
@@ -85,7 +85,7 @@ STATUS_REMOVE_HEIGHT = 40
STATUS_METRIC_GAP = 18
STATUS_SELECTION_CHIP_HEIGHT = 30
MAPS_TILE_GREEN = rl.Color(16, 185, 129, 255)
MAPS_PANEL_STYLE = replace(
PANEL_STYLE = replace(
DEFAULT_PANEL_STYLE,
accent=MAPS_TILE_GREEN,
current_fill=rl.Color(16, 185, 129, 16),
@@ -180,7 +180,7 @@ class MapStatusCard(Widget):
self._controller._on_remove()
def _render(self, rect: rl.Rectangle):
draw_soft_card(rect, MAPS_PANEL_STYLE.surface_fill, MAPS_PANEL_STYLE.surface_border)
draw_soft_card(rect, PANEL_STYLE.surface_fill, PANEL_STYLE.surface_border)
inset = STATUS_CARD_INSET
content_x = rect.x + inset
@@ -233,7 +233,7 @@ class MapStatusCard(Widget):
(tr("Last Updated"), self._controller._last_updated_text()),
],
gap=STATUS_METRIC_GAP,
style=MAPS_PANEL_STYLE,
style=PANEL_STYLE,
label_top_offset=0,
value_top_offset=14,
divider_top_offset=2,
@@ -247,7 +247,7 @@ class MapStatusCard(Widget):
(tr("Last Updated"), self._controller._last_updated_text()),
],
gap=STATUS_METRIC_GAP,
style=MAPS_PANEL_STYLE,
style=PANEL_STYLE,
label_top_offset=0,
value_top_offset=14,
divider_top_offset=2,
@@ -361,7 +361,7 @@ class MapBrowserCard(Widget):
hovered=hovered,
pressed=pressed,
title_size=28,
style=MAPS_PANEL_STYLE,
style=PANEL_STYLE,
)
def _row_height(self, count: int, row_height: float) -> float:
@@ -402,7 +402,7 @@ class MapBrowserCard(Widget):
title_size=24,
subtitle_size=17,
show_underline=True,
style=MAPS_PANEL_STYLE,
style=PANEL_STYLE,
)
def _render_empty_state(self, rect: rl.Rectangle, title: str, body: str):
@@ -416,7 +416,7 @@ class MapBrowserCard(Widget):
title_top_padding=24,
body_height=48,
border=rl.Color(255, 255, 255, 10),
style=MAPS_PANEL_STYLE,
style=PANEL_STYLE,
)
def _render_region_rows(self, rect: rl.Rectangle, regions: list[dict]):
@@ -451,9 +451,9 @@ class MapBrowserCard(Widget):
action_pill_width=132 if selected else 108,
title_size=34,
subtitle_size=22,
row_separator=MAPS_PANEL_STYLE.divider_color,
current_bg=MAPS_PANEL_STYLE.current_fill,
current_border=MAPS_PANEL_STYLE.current_border,
row_separator=PANEL_STYLE.divider_color,
current_bg=PANEL_STYLE.current_fill,
current_border=PANEL_STYLE.current_border,
action_fill=rl.Color(94, 168, 130, 18) if selected else rl.Color(255, 255, 255, 8),
action_border=rl.Color(94, 168, 130, 38) if selected else rl.Color(255, 255, 255, 24),
action_text_color=AetherListColors.HEADER,
@@ -463,7 +463,7 @@ class MapBrowserCard(Widget):
return self._controller._browse_regions_for_active_group()
def _render_section_header(self, rect: rl.Rectangle, title: str, *, count_text: str | None = None):
draw_section_header(rect, title, trailing_text=count_text or "", title_size=28, trailing_size=22, style=MAPS_PANEL_STYLE)
draw_section_header(rect, title, trailing_text=count_text or "", title_size=28, trailing_size=22, style=PANEL_STYLE)
def _measure_height(self, width: float) -> float:
if self._controller._showing_source_picker():
@@ -481,7 +481,7 @@ class MapBrowserCard(Widget):
self.set_rect(rect)
if not self._touch_valid():
self._pressed_target = None
draw_soft_card(rect, MAPS_PANEL_STYLE.surface_fill, MAPS_PANEL_STYLE.surface_border)
draw_soft_card(rect, PANEL_STYLE.surface_fill, PANEL_STYLE.surface_border)
self._source_rects.clear()
self._context_tab_rects.clear()
self._region_row_rects.clear()
@@ -1130,7 +1130,7 @@ class StarPilotMapsLayout(StarPilotPanel):
def _render(self, rect: rl.Rectangle):
self.set_rect(rect)
frame, scroll_rect, content_width = init_list_panel(rect, MAPS_PANEL_STYLE, MAPS_METRICS)
frame, scroll_rect, content_width = init_list_panel(rect, PANEL_STYLE, MAPS_METRICS)
hdr = frame.header
draw_settings_panel_header(hdr, tr("Map Data"), tr("Use offline maps for speed-limit control and keep only the regions you need."),
@@ -12,7 +12,7 @@ import pyray as rl
from openpilot.system.hardware import HARDWARE
from openpilot.system.ui.lib.application import gui_app, FontWeight, MouseEvent, MousePos
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.lib.multilang import tr, tr_noop
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 DialogResult, Widget
@@ -65,17 +65,25 @@ EXCLUDED_KEYS = {
}
REPORT_CATEGORIES = [
"Acceleration feels harsh or jerky",
"An alert was unclear and I'm not sure what it meant",
"Braking is too sudden or uncomfortable",
"I'm not sure if this is normal or a bug:",
"My steering wheel buttons aren't working",
"openpilot disengages when I don't expect it",
"openpilot feels sluggish or slow to respond",
"Something else (please describe)",
tr_noop("Acceleration feels harsh or jerky"),
tr_noop("An alert was unclear and I'm not sure what it meant"),
tr_noop("Braking is too sudden or uncomfortable"),
tr_noop("I'm not sure if this is normal or a bug:"),
tr_noop("My steering wheel buttons aren't working"),
tr_noop("openpilot disengages when I don't expect it"),
tr_noop("openpilot feels sluggish or slow to respond"),
tr_noop("Something else (please describe)"),
]
SECTION_GAP = AETHER_LIST_METRICS.section_gap
SECTION_HEADER_HEIGHT = AETHER_LIST_METRICS.section_header_height
SECTION_HEADER_GAP = AETHER_LIST_METRICS.section_header_gap
ROW_HEIGHT = AETHER_LIST_METRICS.row_height
FADE_HEIGHT = AETHER_LIST_METRICS.fade_height
PANEL_STYLE = DEFAULT_PANEL_STYLE
class SystemSettingsManagerView(Widget):
HEADER_SUBTITLE_HEIGHT = 24
HEADER_SUMMARY_GAP = 12
@@ -83,18 +91,11 @@ class SystemSettingsManagerView(Widget):
TAB_HEIGHT = 56
TAB_GAP = 10
TAB_BOTTOM_GAP = 18
SECTION_GAP = AETHER_LIST_METRICS.section_gap
SECTION_HEADER_HEIGHT = AETHER_LIST_METRICS.section_header_height
SECTION_HEADER_GAP = AETHER_LIST_METRICS.section_header_gap
ROW_HEIGHT = AETHER_LIST_METRICS.row_height
FADE_HEIGHT = AETHER_LIST_METRICS.fade_height
COLUMN_GAP = 22
TWO_COLUMN_BREAKPOINT = 1180
ACTION_PILL_WIDTH = 132
DANGER_PILL_WIDTH = 112
PANEL_STYLE = DEFAULT_PANEL_STYLE
def __init__(self, controller: "StarPilotSystemLayout"):
super().__init__()
self._controller = controller
@@ -216,8 +217,8 @@ class SystemSettingsManagerView(Widget):
presets=spec.get("presets", []),
is_active=lambda key=key: self._active_adjustor_key == key,
set_active=lambda active, key=key: self._set_active_adjustor(key, active),
style=self.PANEL_STYLE,
color=self.PANEL_STYLE.accent,
style=PANEL_STYLE,
color=PANEL_STYLE.accent,
)
)
adjustor.set_touch_valid_callback(lambda adjustor=adjustor: self._scroll_panel.is_touch_valid() or adjustor.is_interacting)
@@ -361,7 +362,7 @@ class SystemSettingsManagerView(Widget):
def _stacked_section_height(self, sections: list[float]) -> float:
if not sections:
return 0.0
return max(0.0, sum(sections) - self.SECTION_GAP)
return sum(sections) + SECTION_GAP * (len(sections) - 1)
def _uses_two_columns(self, width: float) -> bool:
return width >= self.TWO_COLUMN_BREAKPOINT
@@ -490,7 +491,7 @@ class SystemSettingsManagerView(Widget):
def _render(self, rect: rl.Rectangle):
self.set_rect(rect)
frame, scroll_rect, content_width = init_list_panel(rect, self.PANEL_STYLE)
frame, scroll_rect, content_width = init_list_panel(rect, PANEL_STYLE)
self._scroll_rect = scroll_rect
self._drive_mode_control.set_parent_rect(frame.header)
@@ -507,7 +508,7 @@ class SystemSettingsManagerView(Widget):
if self._content_height > scroll_rect.height:
self._scrollbar.render(scroll_rect, self._content_height, self._scroll_offset)
draw_list_scroll_fades(scroll_rect, self._content_height, self._scroll_offset, AetherListColors.PANEL_BG, fade_height=self.FADE_HEIGHT)
draw_list_scroll_fades(scroll_rect, self._content_height, self._scroll_offset, AetherListColors.PANEL_BG, fade_height=FADE_HEIGHT)
def _draw_header(self, rect: rl.Rectangle):
draw_settings_panel_header(rect, tr("System Settings"),
@@ -519,7 +520,7 @@ class SystemSettingsManagerView(Widget):
self._draw_summary_card(summary_rect)
def _draw_summary_card(self, rect: rl.Rectangle):
draw_soft_card(rect, self.PANEL_STYLE.surface_fill, self.PANEL_STYLE.surface_border)
draw_soft_card(rect, PANEL_STYLE.surface_fill, PANEL_STYLE.surface_border)
inset = 18
left_x = rect.x + inset
left_w = rect.width * 0.40
@@ -560,7 +561,7 @@ class SystemSettingsManagerView(Widget):
def _measure_active_tab_height(self, width: float) -> float:
display_h = self._section_block_height(self._slider_section_height(self._display_slider_keys, width))
power_h = self._section_block_height(self._slider_section_height(self._power_slider_keys, width))
backups_h = self._section_block_height(self._section_height(2, self.ROW_HEIGHT))
backups_h = self._section_block_height(self._section_height(2, ROW_HEIGHT))
maintenance_h = self._section_block_height(self._maintenance_section_content_height())
if self._active_tab_key == "basics":
if self._uses_two_columns(width):
@@ -568,7 +569,7 @@ class SystemSettingsManagerView(Widget):
return self._stacked_section_height([display_h, power_h])
if self._active_tab_key == "connectivity":
group_heights = [self._section_block_height(self._section_height(len(self._toggle_defs_for_group(group)), self.ROW_HEIGHT)) for group in self._toggle_groups]
group_heights = [self._section_block_height(self._section_height(len(self._toggle_defs_for_group(group)), ROW_HEIGHT)) for group in self._toggle_groups]
if self._uses_two_columns(width):
return max(group_heights)
return self._stacked_section_height(group_heights)
@@ -578,7 +579,7 @@ class SystemSettingsManagerView(Widget):
return self._stacked_section_height([backups_h, maintenance_h])
def _section_block_height(self, content_height: float) -> float:
return self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP + content_height + self.SECTION_GAP
return SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP + content_height
def _slider_section_height(self, keys: list[str], width: float) -> float:
total = 0.0
@@ -588,8 +589,8 @@ class SystemSettingsManagerView(Widget):
return total
def _maintenance_section_content_height(self) -> float:
support_h = self._section_height(len(self._support_rows), self.ROW_HEIGHT)
danger_h = self._section_height(len(self._danger_rows), self.ROW_HEIGHT)
support_h = self._section_height(len(self._support_rows), ROW_HEIGHT)
danger_h = self._section_height(len(self._danger_rows), ROW_HEIGHT)
return support_h + 12 + 30 + danger_h
def _draw_scroll_content(self, rect: rl.Rectangle, width: float):
@@ -624,7 +625,7 @@ class SystemSettingsManagerView(Widget):
title_size=26,
subtitle_size=17,
show_underline=True,
style=self.PANEL_STYLE,
style=PANEL_STYLE,
)
def _draw_basics_tab(self, y: float, x: float, width: float):
@@ -634,6 +635,7 @@ class SystemSettingsManagerView(Widget):
self._draw_slider_section(y, x + column_w + self.COLUMN_GAP, column_w, tr("Power"), self._power_slider_keys)
return
y = self._draw_slider_section(y, x, width, tr("Display"), self._display_slider_keys)
y += SECTION_GAP
self._draw_slider_section(y, x, width, tr("Power"), self._power_slider_keys)
def _draw_connectivity_tab(self, y: float, x: float, width: float):
@@ -643,8 +645,10 @@ class SystemSettingsManagerView(Widget):
self._draw_toggle_group_section(y, x + column_w + self.COLUMN_GAP, column_w, self._toggle_groups[1])
return
current_y = y
for group in self._toggle_groups:
for i, group in enumerate(self._toggle_groups):
current_y = self._draw_toggle_group_section(current_y, x, width, group)
if i < len(self._toggle_groups) - 1:
current_y += SECTION_GAP
def _draw_care_tab(self, y: float, x: float, width: float):
if self._uses_two_columns(width):
@@ -653,17 +657,18 @@ class SystemSettingsManagerView(Widget):
self._draw_maintenance_section(y, x + column_w + self.COLUMN_GAP, column_w)
return
y = self._draw_backups_section(y, x, width)
y += SECTION_GAP
self._draw_maintenance_section(y, x, width)
def _draw_slider_section(self, y: float, x: float, width: float, title: str, keys: list[str]) -> float:
draw_section_header(rl.Rectangle(x, y, width, self.SECTION_HEADER_HEIGHT), title, style=self.PANEL_STYLE)
y += self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), title, style=PANEL_STYLE)
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
group_rect = rl.Rectangle(x, y, width, self._slider_section_height(keys, width))
draw_list_group_shell(group_rect)
draw_list_group_shell(group_rect, style=PANEL_STYLE)
current_y = group_rect.y
for index, key in enumerate(keys):
current_y = self._draw_slider_row(rl.Rectangle(group_rect.x, current_y, group_rect.width, 0), key, is_last=index == len(keys) - 1)
return y + group_rect.height + self.SECTION_GAP
return y + group_rect.height
def _draw_slider_row(self, rect: rl.Rectangle, key: str, is_last: bool) -> float:
adjustor = self._adjustor_rows[key]
@@ -687,13 +692,13 @@ class SystemSettingsManagerView(Widget):
def _draw_toggle_group_section(self, y: float, x: float, width: float, group: dict) -> float:
toggles = self._toggle_defs_for_group(group)
trailing_text = tr("{} toggles").format(len(toggles))
draw_section_header(rl.Rectangle(x, y, width, self.SECTION_HEADER_HEIGHT), group["title"], trailing_text=trailing_text, style=self.PANEL_STYLE)
y += self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP
toggle_rect = rl.Rectangle(x, y, width, self._section_height(len(toggles), self.ROW_HEIGHT))
draw_list_group_shell(toggle_rect)
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), group["title"], trailing_text=trailing_text, style=PANEL_STYLE)
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
toggle_rect = rl.Rectangle(x, y, width, self._section_height(len(toggles), ROW_HEIGHT))
draw_list_group_shell(toggle_rect, style=PANEL_STYLE)
for index, toggle_def in enumerate(toggles):
self._draw_toggle_row(rl.Rectangle(toggle_rect.x, toggle_rect.y + index * self.ROW_HEIGHT, toggle_rect.width, self.ROW_HEIGHT), toggle_def, is_last=index == len(toggles) - 1)
return y + toggle_rect.height + self.SECTION_GAP
self._draw_toggle_row(rl.Rectangle(toggle_rect.x, toggle_rect.y + index * ROW_HEIGHT, toggle_rect.width, ROW_HEIGHT), toggle_def, is_last=index == len(toggles) - 1)
return y + toggle_rect.height
def _draw_toggle_row(self, rect: rl.Rectangle, toggle_def: dict, is_last: bool):
target_id = f"toggle:{toggle_def['id']}"
@@ -712,18 +717,18 @@ class SystemSettingsManagerView(Widget):
show_chevron=False,
title_size=34,
subtitle_size=22,
style=self.PANEL_STYLE,
style=PANEL_STYLE,
)
def _draw_backups_section(self, y: float, x: float, width: float) -> float:
draw_section_header(rl.Rectangle(x, y, width, self.SECTION_HEADER_HEIGHT), tr("Backups"), trailing_text=self._controller.backup_status_text(), style=self.PANEL_STYLE)
y += self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), tr("Backups"), trailing_text=self._controller.backup_status_text(), style=PANEL_STYLE)
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
summary_rect = rl.Rectangle(x, y, width, self.ROW_HEIGHT * 2)
draw_list_group_shell(summary_rect)
self._draw_backup_manager_row(rl.Rectangle(summary_rect.x, summary_rect.y, summary_rect.width, self.ROW_HEIGHT), "system", is_last=False)
self._draw_backup_manager_row(rl.Rectangle(summary_rect.x, summary_rect.y + self.ROW_HEIGHT, summary_rect.width, self.ROW_HEIGHT), "toggle", is_last=True)
return y + summary_rect.height + self.SECTION_GAP
summary_rect = rl.Rectangle(x, y, width, ROW_HEIGHT * 2)
draw_list_group_shell(summary_rect, style=PANEL_STYLE)
self._draw_backup_manager_row(rl.Rectangle(summary_rect.x, summary_rect.y, summary_rect.width, ROW_HEIGHT), "system", is_last=False)
self._draw_backup_manager_row(rl.Rectangle(summary_rect.x, summary_rect.y + ROW_HEIGHT, summary_rect.width, ROW_HEIGHT), "toggle", is_last=True)
return y + summary_rect.height
def _draw_backup_manager_row(self, rect: rl.Rectangle, backup_kind: str, is_last: bool):
target_id = f"backup:{backup_kind}"
@@ -752,20 +757,20 @@ class SystemSettingsManagerView(Widget):
title_size=34,
subtitle_size=22,
action_text_size=18,
row_separator=self.PANEL_STYLE.divider_color,
row_separator=PANEL_STYLE.divider_color,
action_fill=AetherListColors.CURRENT_BG,
action_border=rl.Color(89, 116, 151, 42),
action_text_color=AetherListColors.HEADER,
)
def _draw_maintenance_section(self, y: float, x: float, width: float):
draw_section_header(rl.Rectangle(x, y, width, self.SECTION_HEADER_HEIGHT), tr("Support & Maintenance"), style=self.PANEL_STYLE)
y += self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP
draw_section_header(rl.Rectangle(x, y, width, SECTION_HEADER_HEIGHT), tr("Support & Maintenance"), style=PANEL_STYLE)
y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP
support_rect = rl.Rectangle(x, y, width, self._section_height(len(self._support_rows), self.ROW_HEIGHT))
draw_list_group_shell(support_rect)
support_rect = rl.Rectangle(x, y, width, self._section_height(len(self._support_rows), ROW_HEIGHT))
draw_list_group_shell(support_rect, style=PANEL_STYLE)
for index, row in enumerate(self._support_rows):
row_rect = rl.Rectangle(support_rect.x, support_rect.y + index * self.ROW_HEIGHT, support_rect.width, self.ROW_HEIGHT)
row_rect = rl.Rectangle(support_rect.x, support_rect.y + index * ROW_HEIGHT, support_rect.width, ROW_HEIGHT)
self._draw_action_row(row_rect, row, is_last=index == len(self._support_rows) - 1)
y += support_rect.height + 12
@@ -773,10 +778,10 @@ class SystemSettingsManagerView(Widget):
gui_label(danger_title_rect, tr("Danger Zone"), 20, AetherListColors.DANGER, FontWeight.MEDIUM)
y += 30
danger_rect = rl.Rectangle(x, y, width, self._section_height(len(self._danger_rows), self.ROW_HEIGHT))
draw_list_group_shell(danger_rect, fill=rl.Color(173, 78, 90, 10), border=rl.Color(173, 78, 90, 30))
danger_rect = rl.Rectangle(x, y, width, self._section_height(len(self._danger_rows), ROW_HEIGHT))
draw_list_group_shell(danger_rect, fill=rl.Color(173, 78, 90, 10), border=rl.Color(173, 78, 90, 30), style=PANEL_STYLE)
for index, row in enumerate(self._danger_rows):
row_rect = rl.Rectangle(danger_rect.x, danger_rect.y + index * self.ROW_HEIGHT, danger_rect.width, self.ROW_HEIGHT)
row_rect = rl.Rectangle(danger_rect.x, danger_rect.y + index * ROW_HEIGHT, danger_rect.width, ROW_HEIGHT)
self._draw_action_row(row_rect, row, is_last=index == len(self._danger_rows) - 1, danger=True)
def _draw_action_row(self, rect: rl.Rectangle, row: dict, is_last: bool, *, danger: bool = False):
@@ -800,7 +805,7 @@ class SystemSettingsManagerView(Widget):
title_size=34,
subtitle_size=22,
action_text_size=18,
row_separator=self.PANEL_STYLE.divider_color,
row_separator=PANEL_STYLE.divider_color,
action_fill=action_fill,
action_border=action_border,
action_text_color=action_text_color,
@@ -10,7 +10,7 @@ from openpilot.system.ui.widgets import DialogResult, Widget
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.widgets.label import gui_label
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import _SettingsPage
from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
AETHER_LIST_METRICS,
AetherListColors,
@@ -38,15 +38,15 @@ from openpilot.selfdrive.ui.mici.layouts.settings.fingerprint_catalog import (
ACTION_OPTIONS = [
{"id": 0, "name": "No Action"},
{"id": 1, "name": "Change Personality", "requires_longitudinal": True},
{"id": 2, "name": "Force Coast", "requires_longitudinal": True},
{"id": 3, "name": "Pause Steering"},
{"id": 4, "name": "Pause Accel/Brake", "requires_longitudinal": True},
{"id": 5, "name": "Toggle Experimental", "requires_longitudinal": True},
{"id": 6, "name": "Toggle Traffic", "requires_longitudinal": True},
{"id": 7, "name": "Toggle Switchback"},
{"id": 8, "name": "Create Bookmark"},
{"id": 0, "name": tr_noop("No Action")},
{"id": 1, "name": tr_noop("Change Personality"), "requires_longitudinal": True},
{"id": 2, "name": tr_noop("Force Coast"), "requires_longitudinal": True},
{"id": 3, "name": tr_noop("Pause Steering")},
{"id": 4, "name": tr_noop("Pause Accel/Brake"), "requires_longitudinal": True},
{"id": 5, "name": tr_noop("Toggle Experimental"), "requires_longitudinal": True},
{"id": 6, "name": tr_noop("Toggle Traffic"), "requires_longitudinal": True},
{"id": 7, "name": tr_noop("Toggle Switchback")},
{"id": 8, "name": tr_noop("Create Bookmark")},
]
ACTION_NAMES = [o["name"] for o in ACTION_OPTIONS]
ACTION_IDS = {o["name"]: o["id"] for o in ACTION_OPTIONS}
@@ -60,6 +60,14 @@ def _lock_doors_timer_labels():
return labels
SECTION_GAP = AETHER_LIST_METRICS.section_gap
SECTION_HEADER_HEIGHT = AETHER_LIST_METRICS.section_header_height
SECTION_HEADER_GAP = AETHER_LIST_METRICS.section_header_gap
ROW_HEIGHT = AETHER_LIST_METRICS.row_height
FADE_HEIGHT = AETHER_LIST_METRICS.fade_height
PANEL_STYLE = DEFAULT_PANEL_STYLE
class VehicleSettingsManagerView(Widget):
HEADER_SUBTITLE_HEIGHT = 24
HEADER_SUMMARY_GAP = 12
@@ -67,15 +75,8 @@ class VehicleSettingsManagerView(Widget):
TAB_HEIGHT = 56
TAB_GAP = 10
TAB_BOTTOM_GAP = 18
SECTION_GAP = AETHER_LIST_METRICS.section_gap
SECTION_HEADER_HEIGHT = AETHER_LIST_METRICS.section_header_height
SECTION_HEADER_GAP = AETHER_LIST_METRICS.section_header_gap
ROW_HEIGHT = AETHER_LIST_METRICS.row_height
FADE_HEIGHT = AETHER_LIST_METRICS.fade_height
COLUMN_GAP = 22
TWO_COLUMN_BREAKPOINT = 1180
PANEL_STYLE = DEFAULT_PANEL_STYLE
COLUMN_GAP = 22
def __init__(self, controller: "StarPilotVehicleSettingsLayout"):
super().__init__()
@@ -109,12 +110,12 @@ class VehicleSettingsManagerView(Widget):
def _section_block_height(self, content_height: float) -> float:
if content_height <= 0:
return 0.0
return self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP + content_height + self.SECTION_GAP
return SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP + content_height
def _stacked_section_height(self, sections: list[float]) -> float:
if not sections:
return 0.0
return max(0.0, sum(sections) - self.SECTION_GAP)
return max(0.0, sum(sections) - SECTION_GAP)
def _interactive_state(self, target_id: str, rect: rl.Rectangle, *, pad_y: float = 0) -> tuple[bool, bool]:
self._interactive_rects[target_id] = rect
@@ -194,9 +195,8 @@ class VehicleSettingsManagerView(Widget):
def _render(self, rect: rl.Rectangle):
self.set_rect(rect)
self._interactive_rects.clear()
frame, scroll_rect, content_width = init_list_panel(rect, self.PANEL_STYLE)
frame, scroll_rect, content_width = init_list_panel(rect, PANEL_STYLE)
self._shell_rect = frame.shell
self._scroll_rect = scroll_rect
@@ -213,7 +213,7 @@ class VehicleSettingsManagerView(Widget):
if self._content_height > scroll_rect.height:
self._scrollbar.render(scroll_rect, self._content_height, self._scroll_offset)
draw_list_scroll_fades(scroll_rect, self._content_height, self._scroll_offset, AetherListColors.PANEL_BG, fade_height=self.FADE_HEIGHT)
draw_list_scroll_fades(scroll_rect, self._content_height, self._scroll_offset, AetherListColors.PANEL_BG, fade_height=FADE_HEIGHT)
def _draw_header(self, rect: rl.Rectangle):
draw_settings_panel_header(rect, tr("Vehicle Settings"),
@@ -225,7 +225,7 @@ class VehicleSettingsManagerView(Widget):
self._draw_summary_card(summary_rect)
def _draw_summary_card(self, rect: rl.Rectangle):
draw_soft_card(rect, self.PANEL_STYLE.surface_fill, self.PANEL_STYLE.surface_border)
draw_soft_card(rect, PANEL_STYLE.surface_fill, PANEL_STYLE.surface_border)
inset = 18
left_x = rect.x + inset
left_w = rect.width * 0.40
@@ -252,7 +252,7 @@ class VehicleSettingsManagerView(Widget):
draw_metric_strip(
rl.Rectangle(left_x, rect.y + 72, max(240.0, rect.width * 0.38), 30),
metrics,
style=self.PANEL_STYLE,
style=PANEL_STYLE,
label_top_offset=0,
value_top_offset=14,
divider_top_offset=2,
@@ -286,19 +286,19 @@ class VehicleSettingsManagerView(Widget):
def _measure_active_tab_height(self, width: float) -> float:
if self._active_tab_key == "identity":
return self._section_block_height(self._section_height(3, self.ROW_HEIGHT))
return self._section_block_height(self._section_height(3, ROW_HEIGHT))
if self._active_tab_key == "features":
rows = self._build_driving_rows()
if self._uses_two_columns(width):
max_per_col = (len(rows) + 1) // 2
return self._section_block_height(self._section_height(max_per_col, self.ROW_HEIGHT))
return self._section_block_height(self._section_height(len(rows), self.ROW_HEIGHT))
return self._section_block_height(self._section_height(max_per_col, ROW_HEIGHT))
return self._section_block_height(self._section_height(len(rows), ROW_HEIGHT))
if self._active_tab_key == "controls":
rows = self._build_steering_rows()
if self._uses_two_columns(width):
max_per_col = (len(rows) + 1) // 2
return self._section_block_height(self._section_height(max_per_col, self.ROW_HEIGHT))
return self._section_block_height(self._section_height(len(rows), self.ROW_HEIGHT))
return self._section_block_height(self._section_height(max_per_col, ROW_HEIGHT))
return self._section_block_height(self._section_height(len(rows), ROW_HEIGHT))
return 0
def _draw_scroll_content(self, rect: rl.Rectangle, width: float):
@@ -333,7 +333,7 @@ class VehicleSettingsManagerView(Widget):
title_size=26,
subtitle_size=17,
show_underline=True,
style=self.PANEL_STYLE,
style=PANEL_STYLE,
)
def _draw_identity_tab(self, y: float, x: float, width: float):
@@ -346,12 +346,12 @@ class VehicleSettingsManagerView(Widget):
"subtitle": tr("Manually select vehicle instead of auto-detecting."),
"get_state": lambda: self._controller._params.get_bool("ForceFingerprint")},
]
draw_section_header(rl.Rectangle(x, y, width, self.SECTION_HEADER_HEIGHT), tr("Vehicle Identity"), style=self.PANEL_STYLE)
y += self.SECTION_HEADER_HEIGHT + self.SECTION_HEADER_GAP
container_rect = rl.Rectangle(x, y, width, len(rows) * self.ROW_HEIGHT)
draw_list_group_shell(container_rect)
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(rows) * ROW_HEIGHT)
draw_list_group_shell(container_rect, style=PANEL_STYLE)
for index, row in enumerate(rows):
row_rect = rl.Rectangle(x, y + index * self.ROW_HEIGHT, width, self.ROW_HEIGHT)
row_rect = rl.Rectangle(x, y + index * ROW_HEIGHT, width, ROW_HEIGHT)
self._draw_row(row_rect, row, is_last=index == len(rows) - 1)
def _draw_features_tab(self, y: float, x: float, width: float):
@@ -381,12 +381,12 @@ class VehicleSettingsManagerView(Widget):
def _draw_row_group(self, y: float, x: float, width: float, rows: list[dict]):
if not rows:
return y
container_rect = rl.Rectangle(x, y, width, len(rows) * self.ROW_HEIGHT)
draw_list_group_shell(container_rect)
container_rect = rl.Rectangle(x, y, width, len(rows) * ROW_HEIGHT)
draw_list_group_shell(container_rect, style=PANEL_STYLE)
for index, row in enumerate(rows):
row_rect = rl.Rectangle(x, y + index * self.ROW_HEIGHT, width, self.ROW_HEIGHT)
row_rect = rl.Rectangle(x, y + index * ROW_HEIGHT, width, ROW_HEIGHT)
self._draw_row(row_rect, row, is_last=index == len(rows) - 1)
return y + len(rows) * self.ROW_HEIGHT + self.SECTION_GAP
return y + len(rows) * ROW_HEIGHT
def _draw_row(self, rect: rl.Rectangle, row: dict, is_last: bool):
target_id = row["target_id"]
@@ -398,7 +398,7 @@ class VehicleSettingsManagerView(Widget):
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=self.PANEL_STYLE,
style=PANEL_STYLE,
)
elif row_type == "select":
draw_selection_list_row(
@@ -407,9 +407,9 @@ class VehicleSettingsManagerView(Widget):
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=self.PANEL_STYLE.divider_color,
action_fill=self.PANEL_STYLE.current_fill,
action_border=self.PANEL_STYLE.current_border,
row_separator=PANEL_STYLE.divider_color,
action_fill=PANEL_STYLE.current_fill,
action_border=PANEL_STYLE.current_border,
action_text_color=AetherListColors.HEADER,
)
elif row_type == "info":
@@ -417,7 +417,7 @@ class VehicleSettingsManagerView(Widget):
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=self.PANEL_STYLE,
style=PANEL_STYLE,
)
def _build_driving_rows(self) -> list[dict]:
@@ -498,22 +498,12 @@ class VehicleSettingsManagerView(Widget):
return rows
class StarPilotVehicleSettingsLayout(StarPilotPanel):
class StarPilotVehicleSettingsLayout(_SettingsPage):
def __init__(self):
super().__init__()
self._make_options, self._models_by_make, self._models_by_value, self._make_by_model = get_fingerprint_catalog()
self._manager_view = VehicleSettingsManagerView(self)
def _render(self, rect: rl.Rectangle):
self._manager_view.render(rect)
def show_event(self):
super().show_event()
self._manager_view.show_event()
def hide_event(self):
super().hide_event()
self._manager_view.hide_event()
def _action_title(self, key: str) -> str:
titles = {
@@ -534,15 +524,15 @@ class StarPilotVehicleSettingsLayout(StarPilotPanel):
if key == "LKASButtonControl" and self._params.get_bool("RemapCancelToDistance"):
if self._params.get_int("LKASButtonControl") != 0:
self._params.put_int("LKASButtonControl", 0)
return ACTION_NAME_BY_ID[0]
return tr(ACTION_NAME_BY_ID[0])
idx = self._params.get_int(key)
return ACTION_NAME_BY_ID.get(idx, ACTION_NAMES[0])
return tr(ACTION_NAME_BY_ID.get(idx, ACTION_NAMES[0]))
def _get_available_actions(self, key: str | None = None) -> list[str]:
if key == "LKASButtonControl" and self._params.get_bool("RemapCancelToDistance"):
return [ACTION_NAME_BY_ID[0]]
return [tr(ACTION_NAME_BY_ID[0])]
cs = starpilot_state.car_state
return [o["name"] for o in ACTION_OPTIONS if cs.hasOpenpilotLongitudinal or not o.get("requires_longitudinal", False)]
return [tr(o["name"]) for o in ACTION_OPTIONS if cs.hasOpenpilotLongitudinal or not o.get("requires_longitudinal", False)]
def _on_toggle(self, param_key: str):
if param_key == "DisableOpenpilotLongitudinal":