Unify Bluetooth and favourites with a searchable action picker

(cherry picked from commit e82b1f0f7a)
This commit is contained in:
AngusBell97
2026-09-10 21:12:20 +01:00
committed by firestar5683
parent 8eb46987ff
commit b942e08f58
20 changed files with 1150 additions and 155 deletions
@@ -9,6 +9,7 @@ from typing import Any
import pyray as rl
from openpilot.starpilot.common.controller_actions import CONTROLLER_ACTION_SET_SPEED
from openpilot.starpilot.common.favorite_slots import (
FAVORITE_SLOT_COUNT,
get_favorite_enum_state,
@@ -257,6 +258,9 @@ class FavoriteRadialMenu:
key = slot.get("key")
if key in self._available_option_labels:
slot["label"] = self._available_option_labels[key]
if key == CONTROLLER_ACTION_SET_SPEED and slot.get("value") is not None:
unit = "km/h" if self._params.get_bool("IsMetric") else "mph"
slot["label"] = f"Set Speed To {slot['value']:g} {unit}"
self._layout_slot_rects()
self._layout_picker_rects()
@@ -590,7 +594,9 @@ class FavoriteRadialMenu:
def _open_picker(self, slot_index: int) -> None:
options = self._refresh_option_catalog()
self._picker_options = options or []
# This on-road picker has no numeric editor. Keep speed actions in the
# catalogue for activating saved slots, but configure their value in Galaxy.
self._picker_options = [option for option in (options or []) if option.get("value_type") != "speed"]
self._selected_slot = slot_index
self._editing_slot = None
self._picker_page = 0
@@ -602,7 +608,7 @@ class FavoriteRadialMenu:
return
key = str(option.get("key") or "").strip()
if not key:
if not key or key == CONTROLLER_ACTION_SET_SPEED or option.get("value_type") == "speed":
return
slots = load_favorite_slots(self._params, eligible_keys=self._available_option_keys)
@@ -1013,7 +1019,7 @@ class FavoriteRadialMenu:
title = f"Assign Favorite {self._selected_slot + 1}" if self._selected_slot is not None else "Assign Favorite"
title_pos = rl.Vector2(self._drawer_rect.x + 36 * scale, self._drawer_rect.y + 26 * scale)
self._draw_text(self._font(bold=True), title, title_pos, int(50 * scale), self._TEXT)
subtitle = "Choose a shortcut"
subtitle = "Set Speed: configure in New Galaxy"
self._draw_text(
self._font(bold=False), subtitle,
rl.Vector2(title_pos.x, title_pos.y + 72 * scale), int(26 * scale), self._MUTED_TEXT,
@@ -933,3 +933,53 @@ def test_render_boolean_toggle_switch_and_picker_badges(monkeypatch):
assert "TOGGLE" in drawn_texts
assert "2 STATES" in drawn_texts
assert "ACTION" in drawn_texts
def test_speed_assignment_requires_new_galaxy_without_disabling_existing_slot():
from openpilot.starpilot.common.controller_actions import CONTROLLER_ACTION_SET_SPEED
speed_option = {"key": CONTROLLER_ACTION_SET_SPEED, "label": "Set Speed To", "value_type": "speed", "default_value": 30}
menu, params, _memory = _menu([0.0], options=[speed_option, {"key": "FeatureToggle", "label": "Feature Toggle"}])
params.put(FAVORITE_SLOTS_PARAM, [{"enabled": True, "show_onroad": True, "key": "FeatureToggle", "label": "Feature Toggle"}])
menu._open_picker(0)
assert CONTROLLER_ACTION_SET_SPEED in menu._available_option_keys
assert [option["key"] for option in menu._picker_options] == ["FeatureToggle"]
menu._assign_option(speed_option)
assert params.get(FAVORITE_SLOTS_PARAM)[0]["key"] == "FeatureToggle"
assert menu.state == FavoriteRadialMenu.STATE_PICKER
def test_saved_speed_favorite_retains_value_and_displays_current_unit():
from openpilot.starpilot.common.controller_actions import CONTROLLER_ACTION_SET_SPEED
menu, params, _memory = _menu([0.0], options=[{"key": CONTROLLER_ACTION_SET_SPEED, "label": "Set Speed To", "value_type": "speed"}])
params.put(FAVORITE_SLOTS_PARAM, [{"enabled": True, "show_onroad": True, "key": CONTROLLER_ACTION_SET_SPEED, "label": "Set Speed To", "value": 42}])
menu._open_radial()
for metric, unit in [(False, "mph"), (True, "km/h")]:
params.put_bool("IsMetric", metric)
menu._layout(rl.Rectangle(0, 0, 2160, 1080))
slot = menu._slots[0]
assert menu._slot_is_configured(slot)
assert slot["value"] == 42
assert slot["label"] == f"Set Speed To 42 {unit}"
assert params.get(FAVORITE_SLOTS_PARAM)[0]["label"] == "Set Speed To"
def test_native_saved_speed_favorite_tap_dispatches_configured_value(monkeypatch):
from openpilot.starpilot.common.controller_actions import CONTROLLER_ACTION_SET_SPEED
from openpilot.starpilot.system.wheel_controls import wheel_controlsd
menu, params, memory = _menu([0.0], options=[{"key": CONTROLLER_ACTION_SET_SPEED, "label": "Set Speed To", "value_type": "speed"}])
slot = {"enabled": True, "show_onroad": True, "key": CONTROLLER_ACTION_SET_SPEED, "label": "Set Speed To", "value": 42}
params.put(FAVORITE_SLOTS_PARAM, [slot])
dispatched = []
monkeypatch.setattr(wheel_controlsd, "execute_controller_key", lambda key, params, memory, **kwargs: dispatched.append((key, kwargs["value"])) or True)
rect = rl.Rectangle(0, 0, 2160, 1080)
_tap(menu, rect, menu.corner_center(rect))
_tap(menu, rect, menu.slot_centers(rect)[0])
assert dispatched == [(CONTROLLER_ACTION_SET_SPEED, 42)]
assert params.get(FAVORITE_SLOTS_PARAM) == [slot]