BigUI WIP: LED

This commit is contained in:
firestarsdog
2026-06-09 21:30:52 -04:00
parent 455a8ec11d
commit 7d53bd5af7
3 changed files with 85 additions and 37 deletions
@@ -636,13 +636,21 @@ class PanelManagerView(AetherInteractiveMixin, Widget):
# active drag
if self._page_drag_active:
self._page_scissor_push(clip_rect)
grid.render(rl.Rectangle(rect.x + self._page_drag_offset, rect.y, rect.width, rect.height))
drag_off = self._page_drag_offset
drag_clip = rl.Rectangle(
clip_rect.x + min(0, drag_off),
clip_rect.y,
clip_rect.width + abs(drag_off),
clip_rect.height,
)
self._page_scissor_push(drag_clip)
grid.render(rl.Rectangle(rect.x + drag_off, rect.y, rect.width, rect.height))
self._page_scissor_pop()
return
# no animation
if not self._page_animating:
grid.set_parent_rect(self._scroll_rect)
grid.render(rect)
return
@@ -652,6 +660,7 @@ class PanelManagerView(AetherInteractiveMixin, Widget):
if elapsed >= duration:
self._page_animating = False
self._page_anim_prev_tiles.clear()
grid.set_parent_rect(self._scroll_rect)
grid.render(rect)
return
@@ -665,8 +674,8 @@ class PanelManagerView(AetherInteractiveMixin, Widget):
old_target = -direction * rect.width
prev_offset = self._page_anim_from + (old_target - self._page_anim_from) * t
self._page_scissor_push(rl.Rectangle(clip_rect.x, clip_rect.y,
clip_rect.width + rect.width, clip_rect.height))
self._page_scissor_push(rl.Rectangle(clip_rect.x - rect.width, clip_rect.y,
clip_rect.width + rect.width * 2, clip_rect.height))
if self._page_anim_prev_tiles:
old_grid = TileGrid(columns=grid.get_column_count(), padding=grid.gap)
old_grid.tiles.extend(self._page_anim_prev_tiles)
@@ -686,7 +695,10 @@ class PanelManagerView(AetherInteractiveMixin, Widget):
def _handle_mouse_press(self, mouse_pos: MousePos) -> None:
super()._handle_mouse_press(mouse_pos)
if self._has_pagination and not self._page_animating:
if self._has_pagination:
if self._page_animating:
self._page_animating = False
self._page_anim_prev_tiles.clear()
self._page_drag_start_x = mouse_pos.x
self._page_drag_start_y = mouse_pos.y
self._page_drag_active = True
@@ -719,11 +731,13 @@ class PanelManagerView(AetherInteractiveMixin, Widget):
self._start_drag_commit(offset)
self._current_page = new_page
self._on_page_changed()
return
elif abs(offset) > 8:
self._start_drag_snap(offset)
return
elif abs(offset) > 8:
self._start_drag_snap(offset)
return
return
super()._handle_mouse_release(mouse_pos)
# ── page indicator ─────────────────────────────────────────
@@ -2787,6 +2801,8 @@ class AetherTile(Widget):
color=AetherListColors.MUTED,
)
return layout
def _render(self, rect: rl.Rectangle):
pass
@@ -2906,6 +2922,7 @@ class ToggleTile(AetherTile):
desc: str = "",
is_enabled: Callable[[], bool] | None = None,
disabled_label: str = "",
show_led: bool = False,
):
if bg_color:
super().__init__(surface_color=bg_color)
@@ -2922,6 +2939,7 @@ class ToggleTile(AetherTile):
self._inactive_color = rl.Color(120, 120, 120, 255)
self._disabled_color = rl.Color(75, 75, 75, 255)
self._disabled_label = disabled_label
self._show_led = show_led
def _handle_mouse_release(self, mouse_pos: MousePos):
if self._is_pressed:
@@ -2947,17 +2965,44 @@ class ToggleTile(AetherTile):
else:
state_text = tr(self._disabled_label) if self._disabled_label else tr("LOCKED")
self._draw_signal_edge(face, self._active_color if enabled and active else self.surface_color, width=TILE_SIGNAL_WIDTH, alpha=62 if enabled and active else 28)
self._render_tile_stack(
face,
title=self.title,
primary=state_text,
desc=self.desc,
title_font=self._font,
primary_font=self._font,
desc_font=self._font_desc,
title_size=28,
primary_size=30,
)
if self._show_led and enabled:
layout = self._render_tile_stack(
face,
title=self.title,
primary="",
desc=self.desc,
title_font=self._font,
primary_font=self._font,
desc_font=self._font_desc,
title_size=28,
primary_size=30,
)
primary_y = layout["primary_y"]
content_pad = SPACING.tile_content
scale = max(0.82, min(1.12, min(face.width / 360.0, face.height / 205.0)))
ps = max(18, int(round(30 * scale)))
cx = face.x + face.width - content_pad - 10
cy = primary_y + ps / 2
if active:
glow_color = _with_alpha(self._active_color, 24)
rl.draw_circle(int(cx), int(cy), 10, glow_color)
rl.draw_circle(int(cx), int(cy), 6, self._active_color)
else:
rl.draw_circle(int(cx), int(cy), 7, rl.Color(14, 16, 22, 255))
rl.draw_ring(rl.Vector2(cx, cy), 5, 6, 0, 360, 24, rl.Color(70, 78, 95, 140))
else:
self._render_tile_stack(
face,
title=self.title,
primary=state_text,
desc=self.desc,
title_font=self._font,
primary_font=self._font,
desc_font=self._font_desc,
title_size=28,
primary_size=30,
)
class ValueTile(AetherTile):
@@ -115,7 +115,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "AdvancedLateralTune",
"title": tr("Advanced Lateral Tuning"),
"subtitle": tr("Advanced steering control changes to fine-tune how openpilot drives."),
"subtitle": tr("Fine-tune steering response and auto-tuning."),
"get": lambda: p.get_bool("AdvancedLateralTune"),
"set": lambda s: p.put_bool("AdvancedLateralTune", s),
})
@@ -123,7 +123,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "AlwaysOnLateral",
"title": tr("Always On Lateral"),
"subtitle": tr("Keep lateral control active even without openpilot engaged."),
"subtitle": tr("Steering stays active when ACC is off."),
"get": lambda: p.get_bool("AlwaysOnLateral"),
"set": lambda s: (_confirm_reboot_toggle(p, "AlwaysOnLateral", s)
if s else p.put_bool("AlwaysOnLateral", False)),
@@ -140,7 +140,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "PauseLateralOnSignal",
"title": tr("Turn Signal Only"),
"subtitle": tr("Only pause steering below the set speed when the turn signal is active."),
"subtitle": tr("Only pause steering when turn signal is active."),
"get": lambda: p.get_bool("PauseLateralOnSignal"),
"set": lambda s: p.put_bool("PauseLateralOnSignal", s),
})
@@ -148,7 +148,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "NudgelessLaneChange",
"title": tr("Auto Lane Changes"),
"subtitle": tr("When the turn signal is on, openpilot will automatically change lanes without a nudge."),
"subtitle": tr("Signal triggers automatic lane change."),
"get": lambda: p.get_bool("NudgelessLaneChange"),
"set": lambda s: p.put_bool("NudgelessLaneChange", s),
})
@@ -156,7 +156,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "OneLaneChange",
"title": tr("One Per Signal"),
"subtitle": tr("Limit automatic lane changes to one per turn-signal activation."),
"subtitle": tr("One lane change per signal activation."),
"get": lambda: p.get_bool("OneLaneChange"),
"set": lambda s: p.put_bool("OneLaneChange", s),
})
@@ -164,7 +164,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "TurnDesires",
"title": tr("Force Turn Desires"),
"subtitle": tr("While driving below the minimum lane change speed with an active turn signal, instruct openpilot to turn."),
"subtitle": tr("Follow turn intent below min lane change speed."),
"get": lambda: p.get_bool("TurnDesires"),
"set": lambda s: (p.put_bool("TurnDesires", s),
_sync_parent(p, "LateralTune", _LATERAL_TUNE_KEYS)),
@@ -173,7 +173,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "NavDesiresAllowed",
"title": tr("Use Route Desires"),
"subtitle": tr("Allow an active navigation route to request keep-left, keep-right, and low-speed turn desires."),
"subtitle": tr("Allow navigation to request lane keep and turns."),
"get": lambda: p.get_bool("NavDesiresAllowed"),
"set": lambda s: p.put_bool("NavDesiresAllowed", s),
})
@@ -181,7 +181,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "NNFF",
"title": tr("NNFF"),
"subtitle": tr("Neural Network FeedForward controller — uses a trained model to predict steering torque."),
"subtitle": tr("Neural net feedforward steering controller."),
"get": lambda: p.get_bool("NNFF"),
"set": lambda s: (p.put_bool("NNFF", s),
s and p.put_bool("NNFFLite", False),
@@ -193,7 +193,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "NNFFLite",
"title": tr("NNFF Lite"),
"subtitle": tr("Lightweight NNFF steering logic when the full model is off."),
"subtitle": tr("Lightweight NNFF when full model is off."),
"get": lambda: p.get_bool("NNFFLite"),
"set": lambda s: (p.put_bool("NNFFLite", s),
_sync_parent(p, "LateralTune", _LATERAL_TUNE_KEYS)),
@@ -204,7 +204,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "ForceAutoTune",
"title": tr("Force Auto-Tune On"),
"subtitle": tr("Force-enable live auto-tuning for friction and lateral acceleration."),
"subtitle": tr("Force-enable live auto-tuning for friction and lateral accel."),
"get": lambda: p.get_bool("ForceAutoTune"),
"set": lambda s: (p.put_bool("ForceAutoTune", s),
s and p.put_bool("ForceAutoTuneOff", False),
@@ -216,7 +216,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "ForceAutoTuneOff",
"title": tr("Force Auto-Tune Off"),
"subtitle": tr("Force-disable live auto-tuning and use your set values instead."),
"subtitle": tr("Force-disable auto-tuning and use your set values."),
"get": lambda: p.get_bool("ForceAutoTuneOff"),
"set": lambda s: (p.put_bool("ForceAutoTuneOff", s),
s and p.put_bool("ForceAutoTune", False),
@@ -228,7 +228,7 @@ class SteeringManagerView(PanelManagerView):
toggles.append({
"key": "ForceTorqueController",
"title": tr("Force Torque Ctrl"),
"subtitle": tr("Use torque-based steering control instead of angle-based for smoother lane keeping, especially in curves."),
"subtitle": tr("Torque-based steering for smoother lane keeping."),
"get": lambda: p.get_bool("ForceTorqueController"),
"set": lambda s: (p.put_bool("ForceTorqueController", s),
_sync_parent(p, "LateralTune", _LATERAL_TUNE_KEYS)),
@@ -249,6 +249,7 @@ class SteeringManagerView(PanelManagerView):
desc=td.get("subtitle", ""),
is_enabled=td.get("enabled", True),
disabled_label=td.get("disabled_label", ""),
show_led=True,
))
def _measure_content_height(self, width: float) -> float:
@@ -402,7 +403,7 @@ class SteeringManagerView(PanelManagerView):
rows.append({
"target_id": "select:SteerDelay",
"title": tr("Actuator Delay"),
"subtitle": tr("The time between openpilot's steering command and the vehicle's response."),
"subtitle": tr("Time between steering command and vehicle response."),
"get_value": lambda: f"{p.get_float('SteerDelay'):.2f}s",
"pill_width": 120,
})
@@ -434,7 +435,7 @@ class SteeringManagerView(PanelManagerView):
rows.append({
"target_id": "select:SteerRatio",
"title": tr("Steer Ratio"),
"subtitle": tr("Adjust the relationship between steering wheel input and road-wheel angle."),
"subtitle": tr("Relationship between steering wheel and road-wheel angle."),
"get_value": lambda: f"{p.get_float('SteerRatio'):.2f}",
"pill_width": 120,
})
@@ -449,7 +450,7 @@ class SteeringManagerView(PanelManagerView):
aol_rows = [{
"target_id": "select:PauseAOLOnBrake",
"title": tr("Pause AOL On Brake"),
"subtitle": tr("Pause Always On Lateral below this speed while the brake is pressed."),
"subtitle": tr("Pause AOL below this speed while brake is pressed."),
"get_value": lambda: f"{p.get_int('PauseAOLOnBrake')} mph",
"pill_width": 140,
}]
@@ -480,14 +481,14 @@ class SteeringManagerView(PanelManagerView):
lc_rows.append({
"target_id": "select:LaneDetectionWidth",
"title": tr("Min Lane Width"),
"subtitle": tr("Prevent automatic lane changes into lanes narrower than this width."),
"subtitle": tr("Prevent lane changes into narrower lanes."),
"get_value": lambda: f"{p.get_float('LaneDetectionWidth'):.1f} ft",
"pill_width": 120,
})
lc_rows.append({
"target_id": "select:LaneChangeSmoothing",
"title": tr("Lane Change Smoothing"),
"subtitle": tr("How smoothly openpilot commits to a lane change. 10 = Stock, 1 = Smoothest."),
"subtitle": tr("Smoothness of lane change commit. 10 = Stock, 1 = Smoothest."),
"get_value": lambda: tr("Stock") if p.get_int("LaneChangeSmoothing") == 10
else f"{p.get_int('LaneChangeSmoothing')}",
"pill_width": 120,
@@ -510,7 +511,7 @@ class SteeringManagerView(PanelManagerView):
paus_rows.append({
"target_id": "select:LateralResumeDelay",
"title": tr("Resume Delay"),
"subtitle": tr("Delay before lateral resumes after the turn signal is turned off. 0 = Off."),
"subtitle": tr("Delay before lateral resumes after signal off. 0 = Off."),
"get_value": lambda: tr("Off") if p.get_float("LateralResumeDelay") == 0
else f"{p.get_float('LateralResumeDelay'):.1f}s",
"pill_width": 120,
@@ -310,6 +310,8 @@ class SystemSettingsManagerView(PanelManagerView):
self._connectivity_tile_grid.add_tile(tile)
self._connectivity_tile_grid.set_touch_valid_callback(lambda: self._scroll_panel.is_touch_valid())
self._child(self._connectivity_tile_grid)
self._page_grid = self._connectivity_tile_grid
self._set_toggle_pages([self._toggle_defs[i:i+4] for i in range(0, len(self._toggle_defs), 4)])
self._drive_mode_control = self._child(
AetherSegmentedControl(
@@ -554,7 +556,7 @@ class SystemSettingsManagerView(PanelManagerView):
def _draw_connectivity_tiles_column(self, y: float, x: float, width: float, height: float):
draw_list_group_shell(rl.Rectangle(x, y, width, height), style=PANEL_STYLE)
self._connectivity_tile_grid.render(rl.Rectangle(x + 12, y + 12, width - 24, height - 24))
self._render_page_grid(self._connectivity_tile_grid, rl.Rectangle(x + 12, y + 12, width - 24, height - 24))
def _draw_connectivity_tiles_section(self, y: float, x: float, width: float):
tile_rows = self._connectivity_tile_grid.get_row_count(len(self._connectivity_tile_grid.tiles), available_width=width)
@@ -562,7 +564,7 @@ class SystemSettingsManagerView(PanelManagerView):
tiles_content_h = tile_rows * 130 + tile_gaps
draw_list_group_shell(rl.Rectangle(x, y, width, tiles_content_h + 24), style=PANEL_STYLE)
self._connectivity_tile_grid.render(rl.Rectangle(x + 12, y + 12, width - 24, tiles_content_h))
self._render_page_grid(self._connectivity_tile_grid, rl.Rectangle(x + 12, y + 12, width - 24, tiles_content_h))
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, SECTION_HEADER_HEIGHT), title, style=PANEL_STYLE)