From 7f18c24850708de0b09a9fd083573cff2d4fbbad Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 24 Sep 2025 20:00:58 -0700 Subject: [PATCH] match style --- selfdrive/ui/layouts/main.py | 3 + selfdrive/ui/layouts/settings/settings.py | 7 +- selfdrive/ui/layouts/sidebar.py | 4 +- selfdrive/ui/ui.cc | 2 +- selfdrive/ui/ui_state.py | 2 +- system/ui/lib/wifi_manager.py | 25 +++++ system/ui/widgets/button.py | 20 +++- system/ui/widgets/network.py | 113 +++++++++++++++++++++- 8 files changed, 163 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 777d2f4c3f..0b46d57bb9 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -34,6 +34,9 @@ class MainLayout(Widget): # Set callbacks self._setup_callbacks() + self._on_settings_clicked() + + def _render(self, _): self._handle_onroad_transition() self._render_main_content() diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index a731a9158c..a2f32640b0 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -11,7 +11,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.network import WifiManagerUI +from openpilot.system.ui.widgets.network import WifiUi # Settings close button SETTINGS_CLOSE_TEXT = "×" @@ -51,7 +51,7 @@ class PanelInfo: class SettingsLayout(Widget): def __init__(self): super().__init__() - self._current_panel = PanelType.DEVICE + self._current_panel = PanelType.NETWORK # Panel configuration wifi_manager = WifiManager() @@ -59,7 +59,7 @@ class SettingsLayout(Widget): self._panels = { PanelType.DEVICE: PanelInfo("Device", DeviceLayout()), - PanelType.NETWORK: PanelInfo("Network", WifiManagerUI(wifi_manager)), + PanelType.NETWORK: PanelInfo("Network", WifiUi(wifi_manager)), PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()), PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()), PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()), @@ -152,6 +152,7 @@ class SettingsLayout(Widget): return False def set_current_panel(self, panel_type: PanelType): + panel_type = PanelType.NETWORK if panel_type != self._current_panel: self._panels[self._current_panel].instance.hide_event() self._current_panel = panel_type diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index cdbfa4c04c..4d47a9878c 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -189,11 +189,11 @@ class Sidebar(Widget): # Draw colored left edge (clipped rounded rectangle) edge_rect = rl.Rectangle(metric_rect.x + 4, metric_rect.y + 4, 100, 118) rl.begin_scissor_mode(int(metric_rect.x + 4), int(metric_rect.y), 18, int(metric_rect.height)) - rl.draw_rectangle_rounded(edge_rect, 0.18, 10, metric.color) + rl.draw_rectangle_rounded(edge_rect, 0.3, 10, metric.color) rl.end_scissor_mode() # Draw border - rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.15, 10, 2, Colors.METRIC_BORDER) + rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.3, 10, 2, Colors.METRIC_BORDER) # Draw label and value labels = [metric.label, metric.value] diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 9ec61b9b81..9fceb5246e 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -145,7 +145,7 @@ void Device::setAwake(bool on) { void Device::resetInteractiveTimeout(int timeout) { if (timeout == -1) { - timeout = (ignition_on ? 10 : 30); + timeout = (ignition_on ? 10 : 300); } interactive_timeout = timeout * UI_FREQ; } diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index c4a2c0ca11..a9bb4468f2 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -156,7 +156,7 @@ class Device: def reset_interactive_timeout(self, timeout: int = -1) -> None: if timeout == -1: - timeout = 10 if ui_state.ignition else 30 + timeout = 10 if ui_state.ignition else 300 self._interaction_time = time.monotonic() + timeout def add_interactive_timeout_callback(self, callback: Callable): diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index af9ae943ea..7ada17b351 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -132,6 +132,7 @@ class WifiManager: # State self._connecting_to_ssid: str = "" + self._ipv4_address: str = "" self._last_network_update: float = 0.0 self._callback_queue: list[Callable] = [] @@ -409,9 +410,33 @@ class WifiManager: networks.sort(key=lambda n: (-n.is_connected, -n.strength, n.ssid.lower())) self._networks = networks + self._update_ipv4_address() + if self._networks_updated is not None: self._enqueue_callback(self._networks_updated, self._networks) + def _update_ipv4_address(self): + if self._wifi_device is None: + cloudlog.warning("No WiFi device found") + return + + self._ipv4_address = "" + + for conn_path in self._get_active_connections(): + conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE) + conn_type = self._router_main.send_and_get_reply(Properties(conn_addr).get('Type')).body[0][1] + if conn_type == '802-11-wireless': + ip4config_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('Ip4Config')).body[0][1] + + if ip4config_path != "/": + ip4config_addr = DBusAddress(ip4config_path, bus_name=NM, interface=NM_IP4_CONFIG_IFACE) + address_data = self._router_main.send_and_get_reply(Properties(ip4config_addr).get('AddressData')).body[0][1] + + for entry in address_data: + if 'address' in entry: + self._ipv4_address = entry['address'][1] + return + def __del__(self): self.stop() diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index eb38f20597..1e3f28eb8e 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -14,6 +14,7 @@ class ButtonStyle(IntEnum): PRIMARY = 1 # For main actions DANGER = 2 # For critical actions, like reboot or delete TRANSPARENT = 3 # For buttons with transparent background and border + TRANSPARENT_WHITE = 3 # For buttons with transparent background and border ACTION = 4 LIST_ACTION = 5 # For list items with action buttons NO_EFFECT = 6 @@ -23,8 +24,6 @@ class ButtonStyle(IntEnum): ICON_PADDING = 15 DEFAULT_BUTTON_FONT_SIZE = 60 -BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) -BUTTON_DISABLED_BACKGROUND_COLOR = rl.Color(51, 51, 51, 255) ACTION_BUTTON_FONT_SIZE = 48 BUTTON_TEXT_COLOR = { @@ -32,6 +31,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.PRIMARY: rl.Color(228, 228, 228, 255), ButtonStyle.DANGER: rl.Color(228, 228, 228, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.TRANSPARENT_WHITE: rl.WHITE, ButtonStyle.ACTION: rl.BLACK, ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), @@ -39,11 +39,16 @@ BUTTON_TEXT_COLOR = { ButtonStyle.FORGET_WIFI: rl.Color(51, 51, 51, 255), } +BUTTON_DISABLED_TEXT_COLORS = { + ButtonStyle.TRANSPARENT_WHITE: rl.WHITE, +} + BUTTON_BACKGROUND_COLORS = { ButtonStyle.NORMAL: rl.Color(51, 51, 51, 255), ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -56,6 +61,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -63,6 +69,10 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.FORGET_WIFI: rl.Color(130, 130, 130, 255), } +BUTTON_DISABLED_BACKGROUND_COLORS = { + ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, +} + _pressed_buttons: set[str] = set() # Track mouse press state globally @@ -156,7 +166,7 @@ def gui_button( # Draw the button text if any if text: - color = BUTTON_TEXT_COLOR[button_style] if is_enabled else BUTTON_DISABLED_TEXT_COLOR + color = BUTTON_TEXT_COLOR[button_style] if is_enabled else BUTTON_DISABLED_TEXT_COLORS.get(button_style, rl.Color(228, 228, 228, 51)) rl.draw_text_ex(font, text, text_pos, font_size, 0, color) return result @@ -198,8 +208,8 @@ class Button(Widget): else: self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] elif self._button_style != ButtonStyle.NO_EFFECT: - self._background_color = BUTTON_DISABLED_BACKGROUND_COLOR - self._label.set_text_color(BUTTON_DISABLED_TEXT_COLOR) + self._background_color = BUTTON_DISABLED_BACKGROUND_COLORS.get(self._button_style, rl.Color(51, 51, 51, 255)) + self._label.set_text_color(BUTTON_DISABLED_TEXT_COLORS.get(self._button_style, rl.Color(228, 228, 228, 51))) def _render(self, _): roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 3e6317a49c..f0068b495a 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -11,6 +11,8 @@ from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.label import TextAlignment, gui_label +from openpilot.system.ui.widgets.scroller import Scroller +from openpilot.system.ui.widgets.list_view import text_item, button_item, dual_button_item, TextAction, ListItem NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 @@ -26,6 +28,11 @@ STRENGTH_ICONS = [ ] +class PanelType(IntEnum): + WIFI = 0 + ADVANCED = 1 + + class UIState(IntEnum): IDLE = 0 CONNECTING = 1 @@ -34,6 +41,109 @@ class UIState(IntEnum): FORGETTING = 4 +class NavButton(Widget): + def __init__(self, text: str): + super().__init__() + self._text = text + self.set_rect(rl.Rectangle(0, 0, 400, 100)) + + def set_text(self, text: str): + self._text = text + + def _render(self, _): + color = rl.Color(74, 74, 74, 255) if self.is_pressed else rl.Color(57, 57, 57, 255) + rl.draw_rectangle_rounded(self._rect, 0.6, 10, color) + gui_label(self.rect, self._text, font_size=60, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + +class WifiUi(Widget): + def __init__(self, wifi_manager: WifiManager): + super().__init__() + self.wifi_manager = wifi_manager + self._current_panel: PanelType = PanelType.WIFI + self._wifi_panel = WifiManagerUI(wifi_manager) + self._advanced_panel = AdvancedNetworkSettings() + self._nav_button = NavButton("Advanced") + self._nav_button.set_click_callback(self._cycle_panel) + + def show_event(self): + self._set_current_panel(PanelType.WIFI) + self._wifi_panel.show_event() + + def hide_event(self): + self._wifi_panel.hide_event() + + def _cycle_panel(self): + if self._current_panel == PanelType.WIFI: + self._set_current_panel(PanelType.ADVANCED) + else: + self._set_current_panel(PanelType.WIFI) + + def _render(self, _): + # subtract button + content_rect = rl.Rectangle(self._rect.x, self._rect.y + self._nav_button.rect.height + 20, + self._rect.width, self._rect.height - self._nav_button.rect.height - 20) + if self._current_panel == PanelType.WIFI: + self._nav_button.set_text("Advanced") + self._nav_button.set_position(self._rect.x + self._rect.width - self._nav_button.rect.width, self._rect.y + 10) + self._wifi_panel.render(content_rect) + else: + self._nav_button.set_text("Back") + self._nav_button.set_position(self._rect.x, self._rect.y + 10) + self._advanced_panel.render(content_rect) + + self._nav_button.render() + + def _set_current_panel(self, panel: PanelType): + self._current_panel = panel + + +class AdvancedNetworkSettings(Widget): + def __init__(self): + super().__init__() + # + # self._params = Params() + # self._select_language_dialog: MultiOptionDialog | None = None + # self._driver_camera: DriverCameraDialog | None = None + # self._pair_device_dialog: PairingDialog | None = None + # self._fcc_dialog: HtmlRenderer | None = None + + # items = self._initialize_items() + + # action = + + action = TextAction(text="", color=rl.Color(170, 170, 170, 255)) + self._ip_address_btn = ListItem(title="IP Address", action_item=action) + + # enable tethering, tethering password, ip address, wifi network metered, hidden network + + items = [ + self._ip_address_btn + ] + + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _initialize_items(self): + items = [ + + text_item("Dongle ID", dongle_id), + # text_item("Serial", serial), + # button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device), + # button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), + # button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt), + # regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), + # button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), + # button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), + # dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt), + ] + # regulatory_btn.set_visible(TICI) + return items + + def _render(self, _): + self._scroller.render(self._rect) + # gui_label(rect, "Advanced Network Settings (Not Implemented)", font_size=50, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + class WifiManagerUI(Widget): def __init__(self, wifi_manager: WifiManager): super().__init__() @@ -213,7 +323,7 @@ class WifiManagerUI(Widget): self._networks = networks for n in self._networks: self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, - button_style=ButtonStyle.NO_EFFECT) + button_style=ButtonStyle.TRANSPARENT_WHITE) self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, font_size=45) @@ -237,6 +347,7 @@ class WifiManagerUI(Widget): self.state = UIState.IDLE + def main(): gui_app.init_window("Wi-Fi Manager") wifi_ui = WifiManagerUI(WifiManager())