Merge remote-tracking branch 'origin/master' into models-panel-rl

# Conflicts:
#	selfdrive/ui/tests/test_ui/raylib_screenshots.py
#	system/ui/sunnypilot/lib/styles.py
#	system/ui/sunnypilot/widgets/list_view.py
#	system/ui/sunnypilot/widgets/toggle.py
This commit is contained in:
discountchubbs
2025-11-22 10:35:44 -08:00
5 changed files with 86 additions and 166 deletions
@@ -41,47 +41,47 @@ def put_update_params(params: Params):
params.put("UpdaterTargetBranch", BRANCH_NAME)
def setup_homescreen(click, pm: PubMaster, scroll=None):
def setup_homescreen(click, pm: PubMaster):
pass
def setup_homescreen_update_available(click, pm: PubMaster, scroll=None):
def setup_homescreen_update_available(click, pm: PubMaster):
params = Params()
params.put_bool("UpdateAvailable", True)
put_update_params(params)
setup_offroad_alert(click, pm)
def setup_settings(click, pm: PubMaster, scroll=None):
def setup_settings(click, pm: PubMaster):
click(100, 100)
def close_settings(click, pm: PubMaster, scroll=None):
def close_settings(click, pm: PubMaster):
click(240, 216)
def setup_settings_network(click, pm: PubMaster, scroll=None):
def setup_settings_network(click, pm: PubMaster):
setup_settings(click, pm)
click(278, 450)
def setup_settings_network_advanced(click, pm: PubMaster, scroll=None):
setup_settings_network(click, pm, scroll=scroll)
def setup_settings_network_advanced(click, pm: PubMaster):
setup_settings_network(click, pm)
click(1880, 100)
def setup_settings_toggles(click, pm: PubMaster, scroll=None):
def setup_settings_toggles(click, pm: PubMaster):
setup_settings(click, pm)
click(278, 600)
def setup_settings_software(click, pm: PubMaster):
put_update_params(Params())
setup_settings(click, pm)
click(278, 720)
def setup_settings_software(click, pm: PubMaster, scroll=None):
put_update_params(Params())
setup_settings(click, pm)
click(278, 845)
def setup_settings_software_download(click, pm: PubMaster, scroll=None):
def setup_settings_software_download(click, pm: PubMaster):
params = Params()
# setup_settings_software but with "DOWNLOAD" button to test long text
params.put("UpdaterState", "idle")
@@ -89,13 +89,13 @@ def setup_settings_software_download(click, pm: PubMaster, scroll=None):
setup_settings_software(click, pm)
def setup_settings_software_release_notes(click, pm: PubMaster, scroll=None):
setup_settings_software(click, pm, scroll=scroll)
def setup_settings_software_release_notes(click, pm: PubMaster):
setup_settings_software(click, pm)
click(588, 110) # expand description for current version
def setup_settings_software_branch_switcher(click, pm: PubMaster, scroll=None):
setup_settings_software(click, pm, scroll=scroll)
def setup_settings_software_branch_switcher(click, pm: PubMaster):
setup_settings_software(click, pm)
params = Params()
params.put("UpdaterAvailableBranches", f"master,nightly,release,{BRANCH_NAME}")
params.put("GitBranch", BRANCH_NAME) # should be on top
@@ -103,32 +103,30 @@ def setup_settings_software_branch_switcher(click, pm: PubMaster, scroll=None):
click(1984, 449)
def setup_settings_firehose(click, pm: PubMaster, scroll=None):
def setup_settings_firehose(click, pm: PubMaster):
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 950)
click(278, 845)
def setup_settings_developer(click, pm: PubMaster, scroll=None):
def setup_settings_developer(click, pm: PubMaster):
CP = car.CarParams()
CP.alphaLongitudinalAvailable = True # show alpha long control toggle
Params().put("CarParamsPersistent", CP.to_bytes())
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 1040)
click(278, 950)
def setup_keyboard(click, pm: PubMaster, scroll=None):
setup_settings_developer(click, pm, scroll=scroll)
def setup_keyboard(click, pm: PubMaster):
setup_settings_developer(click, pm)
click(1930, 470)
def setup_pair_device(click, pm: PubMaster, scroll=None):
def setup_pair_device(click, pm: PubMaster):
click(1950, 800)
def setup_offroad_alert(click, pm: PubMaster, scroll=None):
def setup_offroad_alert(click, pm: PubMaster):
put_update_params(Params())
set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C')
set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal')
@@ -139,63 +137,22 @@ def setup_offroad_alert(click, pm: PubMaster, scroll=None):
close_settings(click, pm)
def setup_confirmation_dialog(click, pm: PubMaster, scroll=None):
def setup_confirmation_dialog(click, pm: PubMaster):
setup_settings(click, pm)
click(1985, 791) # reset calibration
def setup_experimental_mode_description(click, pm: PubMaster, scroll=None):
def setup_experimental_mode_description(click, pm: PubMaster):
setup_settings_toggles(click, pm)
click(1200, 280) # expand description for experimental mode
def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster, scroll=None):
setup_settings_developer(click, pm, scroll=scroll)
def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster):
setup_settings_developer(click, pm)
click(650, 960) # toggle openpilot longitudinal control
def setup_settings_sunnylink(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 600)
def setup_settings_models(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 950)
def setup_settings_steering(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 1040)
def setup_settings_cruise(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-40, 278, 950)
click(278, 1040)
def setup_settings_visuals(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 330)
def setup_settings_display(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 450)
def setup_settings_osm(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 600)
def setup_settings_trips(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 720)
def setup_settings_vehicle(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-1000, 278, 950)
click(278, 845)
def setup_onroad(click, pm: PubMaster, scroll=None):
def setup_onroad(click, pm: PubMaster):
ds = messaging.new_message('deviceState')
ds.deviceState.started = True
@@ -216,7 +173,7 @@ def setup_onroad(click, pm: PubMaster, scroll=None):
time.sleep(0.05)
def setup_onroad_sidebar(click, pm: PubMaster, scroll=None):
def setup_onroad_sidebar(click, pm: PubMaster):
setup_onroad(click, pm)
click(100, 100) # open sidebar
@@ -235,23 +192,23 @@ def setup_onroad_alert(click, pm: PubMaster, size: log.SelfdriveState.AlertSize,
time.sleep(0.05)
def setup_onroad_small_alert(click, pm: PubMaster, scroll=None):
def setup_onroad_small_alert(click, pm: PubMaster):
setup_onroad_alert(click, pm, AlertSize.small, "Small Alert", "This is a small alert", AlertStatus.normal)
def setup_onroad_medium_alert(click, pm: PubMaster, scroll=None):
def setup_onroad_medium_alert(click, pm: PubMaster):
setup_onroad_alert(click, pm, AlertSize.mid, "Medium Alert", "This is a medium alert", AlertStatus.userPrompt)
def setup_onroad_full_alert(click, pm: PubMaster, scroll=None):
def setup_onroad_full_alert(click, pm: PubMaster):
setup_onroad_alert(click, pm, AlertSize.full, "DISENGAGE IMMEDIATELY", "Driver Distracted", AlertStatus.critical)
def setup_onroad_full_alert_multiline(click, pm: PubMaster, scroll=None):
def setup_onroad_full_alert_multiline(click, pm: PubMaster):
setup_onroad_alert(click, pm, AlertSize.full, "Reverse\nGear", "", AlertStatus.normal)
def setup_onroad_full_alert_long_text(click, pm: PubMaster, scroll=None):
def setup_onroad_full_alert_long_text(click, pm: PubMaster):
setup_onroad_alert(click, pm, AlertSize.full, "TAKE CONTROL IMMEDIATELY", "Calibration Invalid: Remount Device & Recalibrate", AlertStatus.userPrompt)
@@ -286,19 +243,6 @@ CASES = {
"onroad_full_alert_long_text": setup_onroad_full_alert_long_text,
}
# sunnypilot cases
CASES.update({
"settings_sunnylink": setup_settings_sunnylink,
"settings_models": setup_settings_models,
"settings_steering": setup_settings_steering,
"settings_cruise": setup_settings_cruise,
"settings_visuals": setup_settings_visuals,
"settings_display": setup_settings_display,
"settings_osm": setup_settings_osm,
"settings_trips": setup_settings_trips,
"settings_vehicle": setup_settings_vehicle,
})
class TestUI:
def __init__(self):
@@ -332,15 +276,11 @@ class TestUI:
time.sleep(0.01)
pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs)
def scroll(self, clicks, x, y, *args, **kwargs):
pyautogui.scroll(clicks, self.ui.left + x, self.ui.top + y, *args, **kwargs)
time.sleep(UI_DELAY)
@with_processes(["ui"])
def test_ui(self, name, setup_case):
self.setup()
time.sleep(UI_DELAY) # wait for UI to start
setup_case(self.click, self.pm, self.scroll)
setup_case(self.click, self.pm)
self.screenshot(name)
+20 -11
View File
@@ -1,6 +1,14 @@
import pyray as rl
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from dataclasses import dataclass
import pyray as rl
@dataclass
class Base:
# Widget/Control Base Dimensions
@@ -11,20 +19,21 @@ class Base:
ITEM_DESC_V_OFFSET = 150
# Toggle Control
TOGGLE_HEIGHT = 100
TOGGLE_HEIGHT = 120
TOGGLE_WIDTH = int(TOGGLE_HEIGHT * 1.75)
TOGGLE_BG_HEIGHT = TOGGLE_HEIGHT - 20
@dataclass
class SP_Default(Base):
class DefaultStyleSP(Base):
# Base Colors
BASE_BG_COLOR = rl.Color(57, 57, 57, 255) # Grey
ON_BG_COLOR = rl.Color(28, 101, 186, 255) # Blue
OFF_BG_COLOR = BASE_BG_COLOR
ON_HOVER_BG_COLOR = rl.Color(17, 78, 150, 255) # Dark Blue
BASE_BG_COLOR = rl.Color(57, 57, 57, 255) # Grey
ON_BG_COLOR = rl.Color(28, 101, 186, 255) # Blue
OFF_BG_COLOR = rl.Color(70, 70, 70, 255) # Lighter Grey
ON_HOVER_BG_COLOR = rl.Color(17, 78, 150, 255) # Dark Blue
OFF_HOVER_BG_COLOR = rl.Color(21, 21, 21, 255) # Dark gray
DISABLED_ON_BG_COLOR = rl.Color(37, 70, 107, 255) # Dull Blue
DISABLED_OFF_BG_COLOR = rl.Color(39, 39, 39, 255) # Grey
DISABLED_ON_BG_COLOR = rl.Color(37, 70, 107, 255) # Dull Blue
DISABLED_OFF_BG_COLOR = rl.Color(39, 39, 39, 255) # Grey
ITEM_TEXT_COLOR = rl.WHITE
ITEM_DISABLED_TEXT_COLOR = rl.Color(88, 88, 88, 255)
ITEM_DESC_TEXT_COLOR = rl.Color(128, 128, 128, 255)
@@ -35,7 +44,7 @@ class SP_Default(Base):
TOGGLE_KNOB_COLOR = rl.WHITE
TOGGLE_DISABLED_ON_COLOR = DISABLED_ON_BG_COLOR
TOGGLE_DISABLED_OFF_COLOR = DISABLED_OFF_BG_COLOR
TOGGLE_DISABLED_KNOB_COLOR = rl.Color(88, 88, 88, 255) # Lighter Grey
TOGGLE_DISABLED_KNOB_COLOR = rl.Color(88, 88, 88, 255) # Lighter Grey
# Button Control
BUTTON_WIDTH = 250
@@ -50,4 +59,4 @@ class SP_Default(Base):
OPTIONCONTROL_TEXT_PRESSED = rl.WHITE
OPTIONCONTROL_TEXT_DISABLED = ITEM_DISABLED_TEXT_COLOR
style = SP_Default
style = DefaultStyleSP
+10 -2
View File
@@ -1,6 +1,12 @@
import pyray as rl
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from collections.abc import Callable
import pyray as rl
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.sunnypilot.widgets.toggle import ToggleSP
from openpilot.system.ui.widgets.list_view import ListItem, ToggleAction, ItemAction
@@ -14,6 +20,7 @@ class ToggleActionSP(ToggleAction):
ToggleAction.__init__(self, initial_state, width, enabled, callback)
self.toggle = ToggleSP(initial_state=initial_state, callback=callback, param=param)
class ListItemSP(ListItem):
def __init__(self, title: str | Callable[[], str] = "", icon: str | None = None, description: str | Callable[[], str] | None = None,
description_visible: bool = False, callback: Callable | None = None,
@@ -93,8 +100,9 @@ class ListItemSP(ListItem):
)
self._html_renderer.render(description_rect)
def toggle_item_sp(title: str | Callable[[], str], description: str | Callable[[], str] | None = None, initial_state: bool = False,
callback: Callable | None = None, icon: str = "", enabled: bool | Callable[[], bool] = True, param: str | None = None) -> ListItemSP:
callback: Callable | None = None, icon: str = "", enabled: bool | Callable[[], bool] = True, param: str | None = None) -> ListItemSP:
action = ToggleActionSP(initial_state=initial_state, enabled=enabled, callback=callback, param=param)
return ListItemSP(title=title, description=description, action_item=action, icon=icon, callback=callback)
+14 -54
View File
@@ -1,5 +1,12 @@
import pyray as rl
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from collections.abc import Callable
import pyray as rl
from openpilot.common.params import Params
from openpilot.system.ui.lib.application import MousePos
from openpilot.system.ui.widgets.toggle import Toggle
@@ -7,23 +14,24 @@ from openpilot.system.ui.sunnypilot.lib.styles import style
KNOB_PADDING = 5
KNOB_RADIUS = style.TOGGLE_BG_HEIGHT / 2 - KNOB_PADDING
SYMBOL_SIZE = KNOB_RADIUS / 2
class ToggleSP(Toggle):
def __init__(self, initial_state=False, callback: Callable[[bool], None] | None = None, param: str | None = None):
self.param_key = param
self.params = Params()
if self.param_key:
initial_state = self.params.get_bool(self.param_key)
initial_state = self.params.get_bool(self.param_key)
Toggle.__init__(self, initial_state, callback)
def _handle_mouse_release(self, mouse_pos: MousePos):
super()._handle_mouse_release(mouse_pos)
if self._enabled and self.param_key:
self.params.put_bool(self.param_key, self._state)
super()._handle_mouse_release(mouse_pos)
if self._enabled and self.param_key:
self.params.put_bool(self.param_key, self._state)
def _render(self, rect: rl.Rectangle):
self.update()
self._rect.y -= style.ITEM_PADDING / 2
if self._enabled:
bg_color = self._blend_color(style.TOGGLE_OFF_COLOR, style.TOGGLE_ON_COLOR, self._progress)
knob_color = style.TOGGLE_KNOB_COLOR
@@ -34,16 +42,6 @@ class ToggleSP(Toggle):
# Draw background
bg_rect = rl.Rectangle(self._rect.x, self._rect.y, style.TOGGLE_WIDTH, style.TOGGLE_BG_HEIGHT)
# Draw outline first
outline_color = style.TOGGLE_ON_COLOR
if not self._enabled:
# Use a more subtle color for disabled state
outline_color = rl.Color(outline_color.r // 2, outline_color.g // 2, outline_color.b // 2, 255)
# Draw outline by drawing a slightly larger rounded rectangle behind the background
outline_rect = rl.Rectangle(bg_rect.x - 2, bg_rect.y - 2, bg_rect.width + 4, bg_rect.height + 4)
rl.draw_rectangle_rounded(outline_rect, 1.0, 10, outline_color)
# Draw actual background
rl.draw_rectangle_rounded(bg_rect, 1.0, 10, bg_color)
@@ -56,41 +54,3 @@ class ToggleSP(Toggle):
knob_y = self._rect.y + style.TOGGLE_BG_HEIGHT / 2
rl.draw_circle(int(knob_x), int(knob_y), KNOB_RADIUS, knob_color)
if self._state and (self._enabled or self._progress > 0.5):
# Draw checkmark when toggle is ON
start_x = knob_x - SYMBOL_SIZE * 0.8
start_y = knob_y
mid_x = knob_x - SYMBOL_SIZE * 0.1
mid_y = knob_y + SYMBOL_SIZE * 0.6
end_x = knob_x + SYMBOL_SIZE * 0.8
end_y = knob_y - SYMBOL_SIZE * 0.5
rl.draw_line_ex(
rl.Vector2(int(start_x), int(start_y)),
rl.Vector2(int(mid_x), int(mid_y)),
3,
style.TOGGLE_ON_COLOR
)
rl.draw_line_ex(
rl.Vector2(int(mid_x), int(mid_y)),
rl.Vector2(int(end_x), int(end_y)),
3,
style.TOGGLE_ON_COLOR
)
else:
# Draw X when toggle is OFF
x_offset = SYMBOL_SIZE * 0.65
rl.draw_line_ex(
rl.Vector2(int(knob_x - x_offset), int(knob_y - x_offset)),
rl.Vector2(int(knob_x + x_offset), int(knob_y + x_offset)),
3,
style.TOGGLE_OFF_COLOR
)
rl.draw_line_ex(
rl.Vector2(int(knob_x + x_offset), int(knob_y - x_offset)),
rl.Vector2(int(knob_x - x_offset), int(knob_y + x_offset)),
3,
style.TOGGLE_OFF_COLOR
)
+3
View File
@@ -15,6 +15,9 @@ from openpilot.system.ui.widgets.label import gui_label
from openpilot.system.ui.widgets.scroller_tici import Scroller
from openpilot.system.ui.widgets.list_view import ButtonAction, ListItem, MultipleButtonAction, ToggleAction, button_item, text_item
if gui_app.sunnypilot_ui():
from openpilot.system.ui.sunnypilot.widgets.list_view import ToggleActionSP as ToggleAction
# These are only used for AdvancedNetworkSettings, standalone apps just need WifiManagerUI
try:
from openpilot.common.params import Params