Files
StarPilot/openpilot/starpilot/ui/toggles_state.py
T
firestar5683 a82935f326 User interfaces and Android Auto
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: Shane Smiskol <shane@smiskol.com>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: pharmacomaniac <blittle65@gmail.com>

Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

96 lines
3.0 KiB
Python

"""Supplied toggle values and request-only interaction for the offline shell."""
from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum
class ToggleKey(StrEnum):
ENABLED = "enabled"
EXPERIMENTAL = "experimental"
SAFE_MODE = "safe_mode"
DISENGAGE_ACCELERATOR = "disengage_accelerator"
LANE_DEPARTURE = "lane_departure"
ALWAYS_ON_DM = "always_on_dm"
RIGHT_HAND_DRIVING = "right_hand_driving"
RECORD_FRONT = "record_front"
RECORD_AUDIO = "record_audio"
METRIC = "metric"
class Personality(StrEnum):
AGGRESSIVE = "aggressive"
STANDARD = "standard"
RELAXED = "relaxed"
@dataclass(frozen=True)
class TogglesState:
enabled: bool = True
experimental: bool = False
safe_mode: bool = False
disengage_accelerator: bool = False
lane_departure: bool = False
always_on_dm: bool = False
right_hand_driving: bool = False
record_front: bool = False
record_audio: bool = False
metric: bool = False
personality: Personality = Personality.STANDARD
scroll_y: float = 0.0
unavailable: frozenset[ToggleKey] = frozenset()
def __post_init__(self) -> None:
if not 0 <= self.scroll_y <= 1000:
raise ValueError("Toggle scroll position must stay within the content")
@dataclass(frozen=True)
class ToggleRequest:
key: ToggleKey | None = None
value: bool | None = None
personality: Personality | None = None
class TogglesInput:
def __init__(self, emit: Callable[[ToggleRequest], None]):
self.emit = emit
self._pressed: tuple[float, float, ToggleRequest, float] | None = None
def _target(self, x: float, y: float, state: TogglesState) -> ToggleRequest | None:
if not 500 <= x < 2160 or not 50 <= y < 1030:
return None
row = int((y - 50 + state.scroll_y) // 171)
rows = (ToggleKey.ENABLED, ToggleKey.EXPERIMENTAL, ToggleKey.SAFE_MODE,
ToggleKey.DISENGAGE_ACCELERATOR, None, ToggleKey.LANE_DEPARTURE,
ToggleKey.ALWAYS_ON_DM, ToggleKey.RIGHT_HAND_DRIVING, ToggleKey.RECORD_FRONT,
ToggleKey.RECORD_AUDIO, ToggleKey.METRIC)
if 0 <= row < len(rows) and rows[row] is not None and x >= 1790:
key = rows[row]
if key in state.unavailable:
return None
return ToggleRequest(key=key, value=not getattr(state, key.value))
if row == 4 and 1300 <= x < 2110:
index = min(2, int((x - 1300) // 270))
return ToggleRequest(personality=tuple(Personality)[index])
return None
def press(self, x: float, y: float, state: TogglesState) -> None:
target = self._target(x, y, state)
self._pressed = (x, y, target, state.scroll_y) if target else None
def move(self, x: float, y: float, state: TogglesState) -> None:
if self._pressed:
_, _, target, scroll_y = self._pressed
if state.scroll_y != scroll_y or self._target(x, y, state) != target:
self.cancel()
def release(self, x: float, y: float, state: TogglesState) -> None:
self.move(x, y, state)
if self._pressed:
self.emit(self._pressed[2])
self.cancel()
def cancel(self) -> None:
self._pressed = None