You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
6.0 KiB
172 lines
6.0 KiB
import pyray as rl
|
|
from dataclasses import dataclass
|
|
from enum import IntEnum
|
|
from collections.abc import Callable
|
|
from openpilot.common.params import Params
|
|
from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
|
|
from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout
|
|
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, FontWeight, Widget
|
|
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
|
from openpilot.selfdrive.ui.layouts.network import NetworkLayout
|
|
|
|
# Import individual panels
|
|
|
|
SETTINGS_CLOSE_TEXT = "X"
|
|
# Constants
|
|
SIDEBAR_WIDTH = 500
|
|
CLOSE_BTN_SIZE = 200
|
|
NAV_BTN_HEIGHT = 80
|
|
PANEL_MARGIN = 50
|
|
SCROLL_SPEED = 30
|
|
|
|
# Colors
|
|
SIDEBAR_COLOR = rl.BLACK
|
|
PANEL_COLOR = rl.Color(41, 41, 41, 255)
|
|
CLOSE_BTN_COLOR = rl.Color(41, 41, 41, 255)
|
|
CLOSE_BTN_PRESSED = rl.Color(59, 59, 59, 255)
|
|
TEXT_NORMAL = rl.Color(128, 128, 128, 255)
|
|
TEXT_SELECTED = rl.Color(255, 255, 255, 255)
|
|
TEXT_PRESSED = rl.Color(173, 173, 173, 255)
|
|
|
|
|
|
class PanelType(IntEnum):
|
|
DEVICE = 0
|
|
NETWORK = 1
|
|
TOGGLES = 2
|
|
SOFTWARE = 3
|
|
FIREHOSE = 4
|
|
DEVELOPER = 5
|
|
|
|
|
|
@dataclass
|
|
class PanelInfo:
|
|
name: str
|
|
instance: object
|
|
button_rect: rl.Rectangle
|
|
|
|
|
|
class SettingsLayout(Widget):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._params = Params()
|
|
self._current_panel = PanelType.DEVICE
|
|
self._max_scroll = 0.0
|
|
|
|
# Panel configuration
|
|
self._panels = {
|
|
PanelType.DEVICE: PanelInfo("Device", DeviceLayout(), rl.Rectangle(0, 0, 0, 0)),
|
|
PanelType.NETWORK: PanelInfo("Network", NetworkLayout(), rl.Rectangle(0, 0, 0, 0)),
|
|
PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout(), rl.Rectangle(0, 0, 0, 0)),
|
|
PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout(), rl.Rectangle(0, 0, 0, 0)),
|
|
PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout(), rl.Rectangle(0, 0, 0, 0)),
|
|
PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout(), rl.Rectangle(0, 0, 0, 0)),
|
|
}
|
|
|
|
self._font_medium = gui_app.font(FontWeight.MEDIUM)
|
|
self._font_bold = gui_app.font(FontWeight.SEMI_BOLD)
|
|
|
|
# Callbacks
|
|
self._close_callback: Callable | None = None
|
|
|
|
def set_callbacks(self, on_close: Callable):
|
|
self._close_callback = on_close
|
|
|
|
def _render(self, rect: rl.Rectangle):
|
|
# Calculate layout
|
|
sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height)
|
|
panel_rect = rl.Rectangle(rect.x + SIDEBAR_WIDTH, rect.y, rect.width - SIDEBAR_WIDTH, rect.height)
|
|
|
|
# Draw components
|
|
self._draw_sidebar(sidebar_rect)
|
|
self._draw_current_panel(panel_rect)
|
|
|
|
def _draw_sidebar(self, rect: rl.Rectangle):
|
|
rl.draw_rectangle_rec(rect, SIDEBAR_COLOR)
|
|
|
|
# Close button
|
|
close_btn_rect = rl.Rectangle(
|
|
rect.x + (rect.width - CLOSE_BTN_SIZE) / 2, rect.y + 45, CLOSE_BTN_SIZE, 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 = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR
|
|
rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color)
|
|
|
|
close_text_size = measure_text_cached(self._font_bold, SETTINGS_CLOSE_TEXT, 140)
|
|
close_text_pos = rl.Vector2(
|
|
close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2,
|
|
close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2,
|
|
)
|
|
rl.draw_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, close_text_pos, 140, 0, TEXT_SELECTED)
|
|
|
|
# Store close button rect for click detection
|
|
self._close_btn_rect = close_btn_rect
|
|
|
|
# Navigation buttons
|
|
nav_start_y = rect.y + 300
|
|
button_spacing = 20
|
|
|
|
i = 0
|
|
for panel_type, panel_info in self._panels.items():
|
|
button_rect = rl.Rectangle(
|
|
rect.x + 50,
|
|
nav_start_y + i * (NAV_BTN_HEIGHT + button_spacing),
|
|
rect.width - 150, # Right-aligned with margin
|
|
NAV_BTN_HEIGHT,
|
|
)
|
|
|
|
# Button styling
|
|
is_selected = panel_type == self._current_panel
|
|
text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL
|
|
# Draw button text (right-aligned)
|
|
text_size = measure_text_cached(self._font_medium, panel_info.name, 65)
|
|
text_pos = rl.Vector2(
|
|
button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2
|
|
)
|
|
rl.draw_text_ex(self._font_medium, panel_info.name, text_pos, 65, 0, text_color)
|
|
|
|
# Store button rect for click detection
|
|
panel_info.button_rect = button_rect
|
|
i += 1
|
|
|
|
def _draw_current_panel(self, rect: rl.Rectangle):
|
|
rl.draw_rectangle_rounded(
|
|
rl.Rectangle(rect.x + 10, rect.y + 10, rect.width - 20, rect.height - 20), 0.04, 30, PANEL_COLOR
|
|
)
|
|
content_rect = rl.Rectangle(rect.x + PANEL_MARGIN, rect.y + 25, rect.width - (PANEL_MARGIN * 2), rect.height - 50)
|
|
# rl.draw_rectangle_rounded(content_rect, 0.03, 30, PANEL_COLOR)
|
|
panel = self._panels[self._current_panel]
|
|
if panel.instance:
|
|
panel.instance.render(content_rect)
|
|
|
|
def _handle_mouse_release(self, mouse_pos: rl.Vector2) -> 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):
|
|
self._switch_to_panel(panel_type)
|
|
return True
|
|
|
|
return False
|
|
|
|
def _switch_to_panel(self, panel_type: PanelType):
|
|
if panel_type != self._current_panel:
|
|
self._current_panel = panel_type
|
|
|
|
def set_current_panel(self, index: int, param: str = ""):
|
|
panel_types = list(self._panels.keys())
|
|
if 0 <= index < len(panel_types):
|
|
self._switch_to_panel(panel_types[index])
|
|
|
|
def close_settings(self):
|
|
if self._close_callback:
|
|
self._close_callback()
|
|
|