mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-22 02:33:47 +08:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee863481d6 | |||
| 65042f6091 | |||
| 60da05f428 | |||
| 79394ff91c | |||
| f6fc098d16 | |||
| f81d864434 | |||
| a4ce6f1ca7 | |||
| b7524418f3 | |||
| b0caef0ece | |||
| 16e7f3095c | |||
| 6dfd075ea8 | |||
| bd55d151d7 | |||
| 554239013c | |||
| 70dcab68a4 | |||
| b787a838ac | |||
| 21855e8aad | |||
| f5adcb4082 | |||
| 924e5a3211 | |||
| 66d41e606b | |||
| a4ee4ba76d | |||
| 5d3f95d420 | |||
| f8d19fe9dd | |||
| 9d711350c2 | |||
| ed775185f2 | |||
| 7bbbc6588e | |||
| e68c65d15d | |||
| 0db8722221 | |||
| e911de5968 | |||
| a33497ed19 | |||
| 91f2bf3459 | |||
| 7fad2fc189 | |||
| 48d33e98e7 | |||
| 2717d97350 | |||
| 64232397ed | |||
| cea6e00819 | |||
| 0613442ac9 | |||
| e6f5aae246 | |||
| 7032e4a972 | |||
| 5b03369a8f | |||
| 1e0564b484 | |||
| eb94abaa14 | |||
| d7b8ce86ed | |||
| 2d3d104658 | |||
| ded02895f4 | |||
| 9778a925b0 | |||
| a9e57f0a76 | |||
| 712a358c94 | |||
| 423a7d2ed0 | |||
| e4e10d4b87 | |||
| 632e9d13b2 | |||
| 362e9ce04b | |||
| 51d0666c85 | |||
| deda1329a2 | |||
| 4110749cb0 | |||
| 3946e643f6 | |||
| 0c37a38596 | |||
| 9c5acf61c0 | |||
| 121b304fe0 | |||
| 47d848293b |
@@ -10,6 +10,9 @@ from openpilot.selfdrive.ui.ui_state import device, ui_state
|
|||||||
from openpilot.system.ui.widgets import Widget
|
from openpilot.system.ui.widgets import Widget
|
||||||
from openpilot.selfdrive.ui.layouts.onboarding import OnboardingWindow
|
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):
|
class MainState(IntEnum):
|
||||||
HOME = 0
|
HOME = 0
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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)
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
"""
|
||||||
|
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 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 _create_nav_button(self, panel_type: OP.PanelType, panel_info: PanelInfo) -> Widget:
|
||||||
|
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
|
||||||
|
|
||||||
|
return NavButton(self, panel_type, panel_info)
|
||||||
|
|
||||||
|
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 = self._create_nav_button(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
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -41,47 +41,47 @@ def put_update_params(params: Params):
|
|||||||
params.put("UpdaterTargetBranch", BRANCH_NAME)
|
params.put("UpdaterTargetBranch", BRANCH_NAME)
|
||||||
|
|
||||||
|
|
||||||
def setup_homescreen(click, pm: PubMaster):
|
def setup_homescreen(click, pm: PubMaster, scroll=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def setup_homescreen_update_available(click, pm: PubMaster):
|
def setup_homescreen_update_available(click, pm: PubMaster, scroll=None):
|
||||||
params = Params()
|
params = Params()
|
||||||
params.put_bool("UpdateAvailable", True)
|
params.put_bool("UpdateAvailable", True)
|
||||||
put_update_params(params)
|
put_update_params(params)
|
||||||
setup_offroad_alert(click, pm)
|
setup_offroad_alert(click, pm)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings(click, pm: PubMaster):
|
def setup_settings(click, pm: PubMaster, scroll=None):
|
||||||
click(100, 100)
|
click(100, 100)
|
||||||
|
|
||||||
|
|
||||||
def close_settings(click, pm: PubMaster):
|
def close_settings(click, pm: PubMaster, scroll=None):
|
||||||
click(240, 216)
|
click(240, 216)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_network(click, pm: PubMaster):
|
def setup_settings_network(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings(click, pm)
|
setup_settings(click, pm)
|
||||||
click(278, 450)
|
click(278, 450)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_network_advanced(click, pm: PubMaster):
|
def setup_settings_network_advanced(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings_network(click, pm)
|
setup_settings_network(click, pm, scroll=scroll)
|
||||||
click(1880, 100)
|
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)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_software(click, pm: PubMaster):
|
|
||||||
put_update_params(Params())
|
|
||||||
setup_settings(click, pm)
|
setup_settings(click, pm)
|
||||||
click(278, 720)
|
click(278, 720)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_software_download(click, pm: PubMaster):
|
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):
|
||||||
params = Params()
|
params = Params()
|
||||||
# setup_settings_software but with "DOWNLOAD" button to test long text
|
# setup_settings_software but with "DOWNLOAD" button to test long text
|
||||||
params.put("UpdaterState", "idle")
|
params.put("UpdaterState", "idle")
|
||||||
@@ -89,13 +89,13 @@ def setup_settings_software_download(click, pm: PubMaster):
|
|||||||
setup_settings_software(click, pm)
|
setup_settings_software(click, pm)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_software_release_notes(click, pm: PubMaster):
|
def setup_settings_software_release_notes(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings_software(click, pm)
|
setup_settings_software(click, pm, scroll=scroll)
|
||||||
click(588, 110) # expand description for current version
|
click(588, 110) # expand description for current version
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_software_branch_switcher(click, pm: PubMaster):
|
def setup_settings_software_branch_switcher(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings_software(click, pm)
|
setup_settings_software(click, pm, scroll=scroll)
|
||||||
params = Params()
|
params = Params()
|
||||||
params.put("UpdaterAvailableBranches", f"master,nightly,release,{BRANCH_NAME}")
|
params.put("UpdaterAvailableBranches", f"master,nightly,release,{BRANCH_NAME}")
|
||||||
params.put("GitBranch", BRANCH_NAME) # should be on top
|
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)
|
click(1984, 449)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_firehose(click, pm: PubMaster):
|
def setup_settings_firehose(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings(click, pm)
|
setup_settings(click, pm)
|
||||||
click(278, 845)
|
scroll(-1000, 278, 950)
|
||||||
|
click(278, 950)
|
||||||
|
|
||||||
|
|
||||||
def setup_settings_developer(click, pm: PubMaster):
|
def setup_settings_developer(click, pm: PubMaster, scroll=None):
|
||||||
CP = car.CarParams()
|
CP = car.CarParams()
|
||||||
CP.alphaLongitudinalAvailable = True # show alpha long control toggle
|
CP.alphaLongitudinalAvailable = True # show alpha long control toggle
|
||||||
Params().put("CarParamsPersistent", CP.to_bytes())
|
Params().put("CarParamsPersistent", CP.to_bytes())
|
||||||
|
|
||||||
setup_settings(click, pm)
|
setup_settings(click, pm)
|
||||||
click(278, 950)
|
scroll(-1000, 278, 950)
|
||||||
|
click(278, 1040)
|
||||||
|
|
||||||
|
|
||||||
def setup_keyboard(click, pm: PubMaster):
|
def setup_keyboard(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings_developer(click, pm)
|
setup_settings_developer(click, pm, scroll=scroll)
|
||||||
click(1930, 470)
|
click(1930, 470)
|
||||||
|
|
||||||
|
|
||||||
def setup_pair_device(click, pm: PubMaster):
|
def setup_pair_device(click, pm: PubMaster, scroll=None):
|
||||||
click(1950, 800)
|
click(1950, 800)
|
||||||
|
|
||||||
|
|
||||||
def setup_offroad_alert(click, pm: PubMaster):
|
def setup_offroad_alert(click, pm: PubMaster, scroll=None):
|
||||||
put_update_params(Params())
|
put_update_params(Params())
|
||||||
set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C')
|
set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C')
|
||||||
set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal')
|
set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal')
|
||||||
@@ -137,22 +139,63 @@ def setup_offroad_alert(click, pm: PubMaster):
|
|||||||
close_settings(click, pm)
|
close_settings(click, pm)
|
||||||
|
|
||||||
|
|
||||||
def setup_confirmation_dialog(click, pm: PubMaster):
|
def setup_confirmation_dialog(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings(click, pm)
|
setup_settings(click, pm)
|
||||||
click(1985, 791) # reset calibration
|
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)
|
setup_settings_toggles(click, pm)
|
||||||
click(1200, 280) # expand description for experimental mode
|
click(1200, 280) # expand description for experimental mode
|
||||||
|
|
||||||
|
|
||||||
def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster):
|
def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster, scroll=None):
|
||||||
setup_settings_developer(click, pm)
|
setup_settings_developer(click, pm, scroll=scroll)
|
||||||
click(650, 960) # toggle openpilot longitudinal control
|
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_onroad(click, pm: PubMaster):
|
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):
|
||||||
ds = messaging.new_message('deviceState')
|
ds = messaging.new_message('deviceState')
|
||||||
ds.deviceState.started = True
|
ds.deviceState.started = True
|
||||||
|
|
||||||
@@ -173,7 +216,7 @@ def setup_onroad(click, pm: PubMaster):
|
|||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
|
||||||
def setup_onroad_sidebar(click, pm: PubMaster):
|
def setup_onroad_sidebar(click, pm: PubMaster, scroll=None):
|
||||||
setup_onroad(click, pm)
|
setup_onroad(click, pm)
|
||||||
click(100, 100) # open sidebar
|
click(100, 100) # open sidebar
|
||||||
|
|
||||||
@@ -192,23 +235,23 @@ def setup_onroad_alert(click, pm: PubMaster, size: log.SelfdriveState.AlertSize,
|
|||||||
time.sleep(0.05)
|
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)
|
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)
|
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)
|
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)
|
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)
|
setup_onroad_alert(click, pm, AlertSize.full, "TAKE CONTROL IMMEDIATELY", "Calibration Invalid: Remount Device & Recalibrate", AlertStatus.userPrompt)
|
||||||
|
|
||||||
|
|
||||||
@@ -243,6 +286,19 @@ CASES = {
|
|||||||
"onroad_full_alert_long_text": setup_onroad_full_alert_long_text,
|
"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:
|
class TestUI:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -276,11 +332,15 @@ class TestUI:
|
|||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs)
|
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"])
|
@with_processes(["ui"])
|
||||||
def test_ui(self, name, setup_case):
|
def test_ui(self, name, setup_case):
|
||||||
self.setup()
|
self.setup()
|
||||||
time.sleep(UI_DELAY) # wait for UI to start
|
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)
|
self.screenshot(name)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:a258a022f0ab90368327d899ed4fb85b47dd0e35c93508e35851d9c3184528c0
|
||||||
|
size 36382
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:8623dbc5c7dd043a91d98777cb423cfd116014ed6390af6d7d00c1f8dea3c6e8
|
||||||
|
size 1181
|
||||||
@@ -41,8 +41,12 @@ class GuiScrollPanel:
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
rl.draw_rectangle_lines(0, 0, abs(int(self._velocity_filter_y.x)), 10, rl.RED)
|
rl.draw_rectangle_lines(0, 0, abs(int(self._velocity_filter_y.x)), 10, rl.RED)
|
||||||
|
|
||||||
# Handle mouse wheel
|
# Handle mouse wheel only when the mouse cursor is over this panel
|
||||||
self._offset_filter_y.x += rl.get_mouse_wheel_move() * MOUSE_WHEEL_SCROLL_SPEED
|
mouse_wheel = rl.get_mouse_wheel_move()
|
||||||
|
if mouse_wheel != 0:
|
||||||
|
mouse_pos = rl.get_mouse_position()
|
||||||
|
if rl.check_collision_point_rec(mouse_pos, bounds):
|
||||||
|
self._offset_filter_y.x += mouse_wheel * MOUSE_WHEEL_SCROLL_SPEED
|
||||||
|
|
||||||
max_scroll_distance = max(0, content.height - bounds.height)
|
max_scroll_distance = max(0, content.height - bounds.height)
|
||||||
if self._scroll_state == ScrollState.IDLE:
|
if self._scroll_state == ScrollState.IDLE:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class Base:
|
|||||||
ITEM_TEXT_FONT_SIZE = 50
|
ITEM_TEXT_FONT_SIZE = 50
|
||||||
ITEM_DESC_FONT_SIZE = 40
|
ITEM_DESC_FONT_SIZE = 40
|
||||||
ITEM_DESC_V_OFFSET = 150
|
ITEM_DESC_V_OFFSET = 150
|
||||||
|
CLOSE_BTN_SIZE = 160
|
||||||
|
|
||||||
# Toggle Control
|
# Toggle Control
|
||||||
TOGGLE_HEIGHT = 120
|
TOGGLE_HEIGHT = 120
|
||||||
@@ -29,7 +30,7 @@ class DefaultStyleSP(Base):
|
|||||||
# Base Colors
|
# Base Colors
|
||||||
BASE_BG_COLOR = rl.Color(57, 57, 57, 255) # Grey
|
BASE_BG_COLOR = rl.Color(57, 57, 57, 255) # Grey
|
||||||
ON_BG_COLOR = rl.Color(28, 101, 186, 255) # Blue
|
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
|
ON_HOVER_BG_COLOR = rl.Color(17, 78, 150, 255) # Dark Blue
|
||||||
OFF_HOVER_BG_COLOR = rl.Color(21, 21, 21, 255) # Dark gray
|
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_ON_BG_COLOR = rl.Color(37, 70, 107, 255) # Dull Blue
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
|
||||||
|
def normalize(text: str) -> str:
|
||||||
|
return unicodedata.normalize('NFKD', text).encode('ascii', 'ignore').decode('utf-8').lower()
|
||||||
|
|
||||||
|
|
||||||
|
def search_from_list(query: str, items: list[str]) -> list[str]:
|
||||||
|
if not query:
|
||||||
|
return items
|
||||||
|
|
||||||
|
normalized_query = normalize(query)
|
||||||
|
search_terms = [re.sub(r'[^a-z0-9]', '', term) for term in normalized_query.split() if term.strip()]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for item in items:
|
||||||
|
normalized_item = normalize(item)
|
||||||
|
item_with_spaces = re.sub(r'[^a-z0-9\s]', ' ', normalized_item)
|
||||||
|
item_stripped = re.sub(r'[^a-z0-9]', '', normalized_item)
|
||||||
|
|
||||||
|
all_terms_match = True
|
||||||
|
for term in search_terms:
|
||||||
|
if not term:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if term not in item_with_spaces and term not in item_stripped:
|
||||||
|
all_terms_match = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if all_terms_match:
|
||||||
|
results.append(item)
|
||||||
|
|
||||||
|
return results
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
from openpilot.common.params import Params
|
||||||
|
from openpilot.system.ui.lib.application import gui_app
|
||||||
|
from openpilot.system.ui.lib.multilang import tr
|
||||||
|
from openpilot.system.ui.widgets import DialogResult
|
||||||
|
from openpilot.system.ui.widgets.keyboard import Keyboard
|
||||||
|
|
||||||
|
|
||||||
|
class InputDialogSP:
|
||||||
|
def __init__(self, title: str, sub_title: str | None = None, current_text: str = "", param: str | None = None,
|
||||||
|
callback: Callable[[DialogResult, str], None] | None = None,
|
||||||
|
min_text_size: int = 0, password_mode: bool = False):
|
||||||
|
self.callback = callback
|
||||||
|
self.current_text = current_text
|
||||||
|
self.keyboard = Keyboard(max_text_size=255, min_text_size=min_text_size, password_mode=password_mode)
|
||||||
|
self.param = param
|
||||||
|
self._params = Params()
|
||||||
|
self.sub_title = sub_title
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
self.keyboard.reset(min_text_size=self.keyboard._min_text_size)
|
||||||
|
self.keyboard.set_title(tr(self.title), *(tr(self.sub_title),) if self.sub_title else ())
|
||||||
|
self.keyboard.set_text(self.current_text)
|
||||||
|
|
||||||
|
def internal_callback(result: DialogResult):
|
||||||
|
text = self.keyboard.text if result == DialogResult.CONFIRM else ""
|
||||||
|
if result == DialogResult.CONFIRM:
|
||||||
|
if self.param:
|
||||||
|
self._params.put(self.param, text)
|
||||||
|
if self.callback:
|
||||||
|
self.callback(result, text)
|
||||||
|
|
||||||
|
gui_app.set_modal_overlay(self.keyboard, internal_callback)
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openpilot.common.params import Params
|
||||||
|
from openpilot.system.ui.lib.application import gui_app
|
||||||
|
from openpilot.system.ui.widgets.keyboard import Keyboard
|
||||||
|
from openpilot.system.ui.sunnypilot.widgets.input_dialog import InputDialogSP
|
||||||
|
|
||||||
|
os.environ['SDL_VIDEODRIVER'] = 'dummy'
|
||||||
|
if not os.environ.get('CI'):
|
||||||
|
pytest.skip("Test in CI environment, or comment out this flag to test locally", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputDialog:
|
||||||
|
def setup_method(self):
|
||||||
|
self.params = Params()
|
||||||
|
|
||||||
|
def test_input_dialog_int(self):
|
||||||
|
gui_app.init_window("test window")
|
||||||
|
dialog = InputDialogSP("title", current_text="current_text", param="MapTargetVelocities")
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
dialog.keyboard._render_return_status = 1
|
||||||
|
gui_app._handle_modal_overlay()
|
||||||
|
|
||||||
|
assert self.params.get("MapTargetVelocities") == "current_text"
|
||||||
|
|
||||||
|
def test_before_input_dialog(self):
|
||||||
|
gui_app.init_window("test window")
|
||||||
|
current_apn = "gsmapn"
|
||||||
|
# This tests the pre InputDialogSP, where keyboard setup had to be done for every single dialog box you want to use.
|
||||||
|
self.keyboard = Keyboard()
|
||||||
|
self.keyboard.reset(min_text_size=0)
|
||||||
|
self.keyboard.set_title(("Enter APN"), ("networking"))
|
||||||
|
self.keyboard.set_text(current_apn)
|
||||||
|
|
||||||
|
def pre_input_dialog_callback(result):
|
||||||
|
if result == 1:
|
||||||
|
apn = self.keyboard.text.strip()
|
||||||
|
self.params.put("GsmApn", apn)
|
||||||
|
|
||||||
|
gui_app.set_modal_overlay(self.keyboard, pre_input_dialog_callback)
|
||||||
|
self.keyboard.set_text("new_apn")
|
||||||
|
self.keyboard._render_return_status = 1
|
||||||
|
gui_app._handle_modal_overlay()
|
||||||
|
pre_input_dialog_result = self.params.get("GsmApn")
|
||||||
|
|
||||||
|
assert pre_input_dialog_result == "new_apn"
|
||||||
|
|
||||||
|
dialog = InputDialogSP(title="Enter APN", sub_title="networking", current_text=current_apn, param="GsmApn")
|
||||||
|
dialog.show()
|
||||||
|
dialog.keyboard.set_text("new_apn")
|
||||||
|
dialog.keyboard._render_return_status = 1
|
||||||
|
gui_app._handle_modal_overlay()
|
||||||
|
input_dialog_result = self.params.get("GsmApn")
|
||||||
|
|
||||||
|
assert input_dialog_result == "new_apn"
|
||||||
|
assert pre_input_dialog_result == input_dialog_result
|
||||||
Reference in New Issue
Block a user