diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 702854f98a..a020a63b55 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -10,6 +10,9 @@ from openpilot.selfdrive.ui.ui_state import device, ui_state from openpilot.system.ui.widgets import Widget from openpilot.selfdrive.ui.layouts.onboarding import OnboardingWindow +if gui_app.sunnypilot_ui(): + from openpilot.selfdrive.ui.sunnypilot.layouts.settings.settings import SettingsLayoutSP as SettingsLayout + class MainState(IntEnum): HOME = 0 diff --git a/selfdrive/ui/sunnypilot/layouts/settings/cruise.py b/selfdrive/ui/sunnypilot/layouts/settings/cruise.py new file mode 100644 index 0000000000..9439c588d3 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/cruise.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class CruiseLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + + items = [ + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/device.py b/selfdrive/ui/sunnypilot/layouts/settings/device.py new file mode 100644 index 0000000000..081969cf10 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/device.py @@ -0,0 +1,12 @@ +""" +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 openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout + + +class DeviceLayoutSP(DeviceLayout): + def __init__(self): + DeviceLayout.__init__(self) diff --git a/selfdrive/ui/sunnypilot/layouts/settings/display.py b/selfdrive/ui/sunnypilot/layouts/settings/display.py new file mode 100644 index 0000000000..d1d0a661b1 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/display.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class DisplayLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/models.py b/selfdrive/ui/sunnypilot/layouts/settings/models.py new file mode 100644 index 0000000000..add437b127 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/models.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class ModelsLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/navigation.py b/selfdrive/ui/sunnypilot/layouts/settings/navigation.py new file mode 100644 index 0000000000..1f44775bb3 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/navigation.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class NavigationLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/osm.py b/selfdrive/ui/sunnypilot/layouts/settings/osm.py new file mode 100644 index 0000000000..d57a0de9d8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/osm.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class OSMLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/settings.py b/selfdrive/ui/sunnypilot/layouts/settings/settings.py new file mode 100644 index 0000000000..bf174de90b --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/settings.py @@ -0,0 +1,202 @@ +""" +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 +from enum import IntEnum + +import pyray as rl +from openpilot.selfdrive.ui.layouts.settings import settings as OP +from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.device import DeviceLayoutSP +from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout +from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout +from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout +from openpilot.system.ui.lib.application import gui_app, MousePos +from openpilot.system.ui.lib.multilang import tr_noop +from openpilot.system.ui.sunnypilot.lib.styles import style +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.widgets.network import NetworkUI +from openpilot.system.ui.lib.wifi_manager import WifiManager +from openpilot.system.ui.widgets import Widget +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.models import ModelsLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.sunnylink import SunnylinkLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.osm import OSMLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.trips import TripsLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.vehicle import VehicleLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.steering import SteeringLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.cruise import CruiseLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.visuals import VisualsLayout +from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import DisplayLayout + +# from openpilot.selfdrive.ui.sunnypilot.layouts.settings.navigation import NavigationLayout + +OP.PANEL_COLOR = rl.Color(10, 10, 10, 255) +ICON_SIZE = 70 + +OP.PanelType = IntEnum( # type: ignore + "PanelType", + [es.name for es in OP.PanelType] + [ + "SUNNYLINK", + "MODELS", + "STEERING", + "CRUISE", + "VISUALS", + "DISPLAY", + "OSM", + "NAVIGATION", + "TRIPS", + "VEHICLE", + ], + start=0, +) + + +@dataclass +class PanelInfo(OP.PanelInfo): + icon: str = "" + + +class NavButton(Widget): + def __init__(self, parent, p_type, p_info): + super().__init__() + self.parent = parent + self.panel_type = p_type + self.panel_info = p_info + + def _render(self, rect): + is_selected = self.panel_type == self.parent._current_panel + text_color = OP.TEXT_SELECTED if is_selected else OP.TEXT_NORMAL + content_x = rect.x + 90 + text_size = measure_text_cached(self.parent._font_medium, self.panel_info.name, 65) + + # Draw background if selected + if is_selected: + self.container_rect = rl.Rectangle( + content_x - 50, rect.y, OP.SIDEBAR_WIDTH - 50, OP.NAV_BTN_HEIGHT + ) + rl.draw_rectangle_rounded(self.container_rect, 0.2, 5, OP.CLOSE_BTN_COLOR) + + if self.panel_info.icon: + icon_texture = gui_app.texture(self.panel_info.icon, ICON_SIZE, ICON_SIZE, keep_aspect_ratio=True) + rl.draw_texture(icon_texture, int(content_x), int(rect.y + (OP.NAV_BTN_HEIGHT - icon_texture.height) / 2), + rl.WHITE) + content_x += ICON_SIZE + 20 + + # Draw button text (right-aligned) + text_pos = rl.Vector2( + content_x, + rect.y + (OP.NAV_BTN_HEIGHT - text_size.y) / 2 + ) + rl.draw_text_ex(self.parent._font_medium, self.panel_info.name, text_pos, 55, 0, text_color) + + # Store button rect for click detection + self.panel_info.button_rect = rect + + +class SettingsLayoutSP(OP.SettingsLayout): + def __init__(self): + OP.SettingsLayout.__init__(self) + self._nav_items: list[Widget] = [] + + # Create sidebar scroller + self._sidebar_scroller = Scroller([], spacing=0, line_separator=False, pad_end=False) + + # Panel configuration + wifi_manager = WifiManager() + wifi_manager.set_active(False) + + self._panels = { + OP.PanelType.DEVICE: PanelInfo(tr_noop("Device"), DeviceLayoutSP(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_home.png"), + OP.PanelType.NETWORK: PanelInfo(tr_noop("Network"), NetworkUI(wifi_manager), icon="icons/network.png"), + OP.PanelType.SUNNYLINK: PanelInfo(tr_noop("sunnylink"), SunnylinkLayout(), icon="icons/shell.png"), + OP.PanelType.TOGGLES: PanelInfo(tr_noop("Toggles"), TogglesLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"), + OP.PanelType.SOFTWARE: PanelInfo(tr_noop("Software"), SoftwareLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_software.png"), + OP.PanelType.MODELS: PanelInfo(tr_noop("Models"), ModelsLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_models.png"), + OP.PanelType.STEERING: PanelInfo(tr_noop("Steering"), SteeringLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"), + OP.PanelType.CRUISE: PanelInfo(tr_noop("Cruise"), CruiseLayout(), icon="icons/speed_limit.png"), + OP.PanelType.VISUALS: PanelInfo(tr_noop("Visuals"), VisualsLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_visuals.png"), + OP.PanelType.DISPLAY: PanelInfo(tr_noop("Display"), DisplayLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_display.png"), + OP.PanelType.OSM: PanelInfo(tr_noop("OSM"), OSMLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_map.png"), + # OP.PanelType.NAVIGATION: PanelInfo(tr_noop("Navigation"), NavigationLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_map.png"), + OP.PanelType.TRIPS: PanelInfo(tr_noop("Trips"), TripsLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"), + OP.PanelType.VEHICLE: PanelInfo(tr_noop("Vehicle"), VehicleLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"), + OP.PanelType.FIREHOSE: PanelInfo(tr_noop("Firehose"), FirehoseLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_firehose.png"), + OP.PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout(), icon="icons/shell.png"), + } + + def _draw_sidebar(self, rect: rl.Rectangle): + rl.draw_rectangle_rec(rect, OP.SIDEBAR_COLOR) + + # Close button + close_btn_rect = rl.Rectangle( + rect.x + style.ITEM_PADDING * 3, rect.y + style.ITEM_PADDING * 2, style.CLOSE_BTN_SIZE, style.CLOSE_BTN_SIZE + ) + + pressed = (rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and + rl.check_collision_point_rec(rl.get_mouse_position(), close_btn_rect)) + close_color = OP.CLOSE_BTN_PRESSED if pressed else OP.CLOSE_BTN_COLOR + rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color) + + icon_color = rl.Color(255, 255, 255, 255) if not pressed else rl.Color(220, 220, 220, 255) + icon_dest = rl.Rectangle( + close_btn_rect.x + (close_btn_rect.width - self._close_icon.width) / 2, + close_btn_rect.y + (close_btn_rect.height - self._close_icon.height) / 2, + self._close_icon.width, + self._close_icon.height, + ) + rl.draw_texture_pro( + self._close_icon, + rl.Rectangle(0, 0, self._close_icon.width, self._close_icon.height), + icon_dest, + rl.Vector2(0, 0), + 0, + icon_color, + ) + + # Store close button rect for click detection + self._close_btn_rect = close_btn_rect + + # Navigation buttons with scroller + if not self._nav_items: + for panel_type, panel_info in self._panels.items(): + nav_button = NavButton(self, panel_type, panel_info) + nav_button.rect.width = rect.width - 100 # Full width minus padding + nav_button.rect.height = OP.NAV_BTN_HEIGHT + self._nav_items.append(nav_button) + self._sidebar_scroller.add_widget(nav_button) + + # Draw navigation section with scroller + nav_rect = rl.Rectangle( + rect.x, + self._close_btn_rect.height + style.ITEM_PADDING * 4, # Starting Y position for nav items + rect.width, + rect.height - 300 # Remaining height after close button + ) + + if self._nav_items: + self._sidebar_scroller.render(nav_rect) + return + + def _handle_mouse_release(self, mouse_pos: MousePos) -> bool: + # Check close button + if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect): + if self._close_callback: + self._close_callback() + return True + + # Check navigation buttons + for panel_type, panel_info in self._panels.items(): + if rl.check_collision_point_rec(mouse_pos, panel_info.button_rect) and self._sidebar_scroller.scroll_panel.is_touch_valid(): + self.set_current_panel(panel_type) + return True + + return False + + def show_event(self): + super().show_event() + self._panels[self._current_panel].instance.show_event() + self._sidebar_scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/steering.py b/selfdrive/ui/sunnypilot/layouts/settings/steering.py new file mode 100644 index 0000000000..ac67eb9163 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/steering.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class SteeringLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/sunnylink.py b/selfdrive/ui/sunnypilot/layouts/settings/sunnylink.py new file mode 100644 index 0000000000..b1e12c17ab --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/sunnylink.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class SunnylinkLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/trips.py b/selfdrive/ui/sunnypilot/layouts/settings/trips.py new file mode 100644 index 0000000000..a318cebeb0 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/trips.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class TripsLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/vehicle.py b/selfdrive/ui/sunnypilot/layouts/settings/vehicle.py new file mode 100644 index 0000000000..d04816a411 --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/vehicle.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class VehicleLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/sunnypilot/layouts/settings/visuals.py b/selfdrive/ui/sunnypilot/layouts/settings/visuals.py new file mode 100644 index 0000000000..1036af3e5f --- /dev/null +++ b/selfdrive/ui/sunnypilot/layouts/settings/visuals.py @@ -0,0 +1,30 @@ +""" +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 openpilot.common.params import Params +from openpilot.system.ui.widgets.scroller_tici import Scroller +from openpilot.system.ui.widgets import Widget + + +class VisualsLayout(Widget): + def __init__(self): + super().__init__() + + self._params = Params() + items = self._initialize_items() + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + ] + return items + + def _render(self, rect): + self._scroller.render(rect) + + def show_event(self): + self._scroller.show_event() diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index d3de616cc0..cd383e4bc3 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -41,47 +41,47 @@ def put_update_params(params: Params): params.put("UpdaterTargetBranch", BRANCH_NAME) -def setup_homescreen(click, pm: PubMaster): +def setup_homescreen(click, pm: PubMaster, scroll=None): pass -def setup_homescreen_update_available(click, pm: PubMaster): +def setup_homescreen_update_available(click, pm: PubMaster, scroll=None): params = Params() params.put_bool("UpdateAvailable", True) put_update_params(params) setup_offroad_alert(click, pm) -def setup_settings(click, pm: PubMaster): +def setup_settings(click, pm: PubMaster, scroll=None): click(100, 100) -def close_settings(click, pm: PubMaster): - click(240, 216) +def close_settings(click, pm: PubMaster, scroll=None): + click(140, 120) -def setup_settings_network(click, pm: PubMaster): +def setup_settings_network(click, pm: PubMaster, scroll=None): setup_settings(click, pm) click(278, 450) -def setup_settings_network_advanced(click, pm: PubMaster): - setup_settings_network(click, pm) +def setup_settings_network_advanced(click, pm: PubMaster, scroll=None): + setup_settings_network(click, pm, scroll=scroll) click(1880, 100) -def setup_settings_toggles(click, pm: PubMaster): +def setup_settings_toggles(click, pm: PubMaster, scroll=None): setup_settings(click, pm) - click(278, 600) + click(278, 620) -def setup_settings_software(click, pm: PubMaster): +def setup_settings_software(click, pm: PubMaster, scroll=None): put_update_params(Params()) setup_settings(click, pm) - click(278, 720) + click(278, 730) -def setup_settings_software_download(click, pm: PubMaster): +def setup_settings_software_download(click, pm: PubMaster, scroll=None): 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): setup_settings_software(click, pm) -def setup_settings_software_release_notes(click, pm: PubMaster): - setup_settings_software(click, pm) +def setup_settings_software_release_notes(click, pm: PubMaster, scroll=None): + setup_settings_software(click, pm, scroll=scroll) click(588, 110) # expand description for current version -def setup_settings_software_branch_switcher(click, pm: PubMaster): - setup_settings_software(click, pm) +def setup_settings_software_branch_switcher(click, pm: PubMaster, scroll=None): + setup_settings_software(click, pm, scroll=scroll) params = Params() params.put("UpdaterAvailableBranches", f"master,nightly,release,{BRANCH_NAME}") params.put("GitBranch", BRANCH_NAME) # should be on top @@ -103,30 +103,32 @@ def setup_settings_software_branch_switcher(click, pm: PubMaster): click(1984, 449) -def setup_settings_firehose(click, pm: PubMaster): +def setup_settings_firehose(click, pm: PubMaster, scroll=None): setup_settings(click, pm) - click(278, 845) + scroll(-1000, 278, 950) + click(278, 850) -def setup_settings_developer(click, pm: PubMaster): +def setup_settings_developer(click, pm: PubMaster, scroll=None): 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, 950) -def setup_keyboard(click, pm: PubMaster): - setup_settings_developer(click, pm) +def setup_keyboard(click, pm: PubMaster, scroll=None): + setup_settings_developer(click, pm, scroll=scroll) click(1930, 470) -def setup_pair_device(click, pm: PubMaster): +def setup_pair_device(click, pm: PubMaster, scroll=None): click(1950, 800) -def setup_offroad_alert(click, pm: PubMaster): +def setup_offroad_alert(click, pm: PubMaster, scroll=None): put_update_params(Params()) set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C') set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal') @@ -137,22 +139,73 @@ def setup_offroad_alert(click, pm: PubMaster): close_settings(click, pm) -def setup_confirmation_dialog(click, pm: PubMaster): +def setup_confirmation_dialog(click, pm: PubMaster, scroll=None): setup_settings(click, pm) click(1985, 791) # reset calibration -def setup_experimental_mode_description(click, pm: PubMaster): +def setup_experimental_mode_description(click, pm: PubMaster, scroll=None): setup_settings_toggles(click, pm) click(1200, 280) # expand description for experimental mode -def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster): - setup_settings_developer(click, pm) +def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster, scroll=None): + setup_settings_developer(click, pm, scroll=scroll) click(650, 960) # toggle openpilot longitudinal control -def setup_onroad(click, pm: PubMaster): +def setup_settings_sunnylink(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + click(278, 510) + + +def setup_settings_models(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + click(278, 840) + + +def setup_settings_steering(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + click(278, 950) + + +def setup_settings_cruise(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + click(278, 1017) + scroll(-140, 278, 950) + + +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, 420) + + +def setup_settings_osm(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + scroll(-1000, 278, 950) + click(278, 520) + + +def setup_settings_trips(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + scroll(-1000, 278, 950) + click(278, 630) + + +def setup_settings_vehicle(click, pm: PubMaster, scroll=None): + setup_settings(click, pm) + scroll(-1000, 278, 950) + click(278, 750) + + +def setup_onroad(click, pm: PubMaster, scroll=None): ds = messaging.new_message('deviceState') ds.deviceState.started = True @@ -173,7 +226,7 @@ def setup_onroad(click, pm: PubMaster): time.sleep(0.05) -def setup_onroad_sidebar(click, pm: PubMaster): +def setup_onroad_sidebar(click, pm: PubMaster, scroll=None): setup_onroad(click, pm) click(100, 100) # open sidebar @@ -192,23 +245,23 @@ def setup_onroad_alert(click, pm: PubMaster, size: log.SelfdriveState.AlertSize, time.sleep(0.05) -def setup_onroad_small_alert(click, pm: PubMaster): +def setup_onroad_small_alert(click, pm: PubMaster, scroll=None): setup_onroad_alert(click, pm, AlertSize.small, "Small Alert", "This is a small alert", AlertStatus.normal) -def setup_onroad_medium_alert(click, pm: PubMaster): +def setup_onroad_medium_alert(click, pm: PubMaster, scroll=None): setup_onroad_alert(click, pm, AlertSize.mid, "Medium Alert", "This is a medium alert", AlertStatus.userPrompt) -def setup_onroad_full_alert(click, pm: PubMaster): +def setup_onroad_full_alert(click, pm: PubMaster, scroll=None): setup_onroad_alert(click, pm, AlertSize.full, "DISENGAGE IMMEDIATELY", "Driver Distracted", AlertStatus.critical) -def setup_onroad_full_alert_multiline(click, pm: PubMaster): +def setup_onroad_full_alert_multiline(click, pm: PubMaster, scroll=None): setup_onroad_alert(click, pm, AlertSize.full, "Reverse\nGear", "", AlertStatus.normal) -def setup_onroad_full_alert_long_text(click, pm: PubMaster): +def setup_onroad_full_alert_long_text(click, pm: PubMaster, scroll=None): setup_onroad_alert(click, pm, AlertSize.full, "TAKE CONTROL IMMEDIATELY", "Calibration Invalid: Remount Device & Recalibrate", AlertStatus.userPrompt) @@ -243,6 +296,19 @@ 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): @@ -276,11 +342,15 @@ 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) + setup_case(self.click, self.pm, self.scroll) self.screenshot(name) diff --git a/sunnypilot/selfdrive/assets/offroad/icon_firehose.png b/sunnypilot/selfdrive/assets/offroad/icon_firehose.png new file mode 100644 index 0000000000..d579937f3f --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_firehose.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a258a022f0ab90368327d899ed4fb85b47dd0e35c93508e35851d9c3184528c0 +size 36382 diff --git a/sunnypilot/selfdrive/assets/offroad/icon_home.png b/sunnypilot/selfdrive/assets/offroad/icon_home.png new file mode 100644 index 0000000000..1229af846d --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_home.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8623dbc5c7dd043a91d98777cb423cfd116014ed6390af6d7d00c1f8dea3c6e8 +size 1181 diff --git a/system/ui/sunnypilot/lib/styles.py b/system/ui/sunnypilot/lib/styles.py index a232ac192d..4880ad58de 100644 --- a/system/ui/sunnypilot/lib/styles.py +++ b/system/ui/sunnypilot/lib/styles.py @@ -17,6 +17,7 @@ class Base: ITEM_TEXT_FONT_SIZE = 50 ITEM_DESC_FONT_SIZE = 40 ITEM_DESC_V_OFFSET = 150 + CLOSE_BTN_SIZE = 160 # Toggle Control TOGGLE_HEIGHT = 120 @@ -29,7 +30,7 @@ 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 = rl.Color(70, 70, 70, 255) # Lighter Grey + OFF_BG_COLOR = BASE_BG_COLOR 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