From 3cc9d89d4578b1b42f1ecfee87aa38bff7ecb3d6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 4 Mar 2026 23:07:37 -0800 Subject: [PATCH] mici ui: wifi scanning card (#37564) * start * yes * no more show * clean up --- .../mici/layouts/settings/network/wifi_ui.py | 76 +++++++++---------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py b/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py index 22d3d1d0d..9cd4db7bf 100644 --- a/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py +++ b/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py @@ -3,7 +3,6 @@ import numpy as np import pyray as rl from collections.abc import Callable -from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.mici.widgets.dialog import BigInputDialog, BigConfirmationDialogV2 from openpilot.selfdrive.ui.mici.widgets.button import BigButton, LABEL_COLOR @@ -14,39 +13,26 @@ from openpilot.system.ui.lib.wifi_manager import WifiManager, Network, SecurityT class LoadingAnimation(Widget): - HIDE_TIME = 4 + RADIUS = 8 + SPACING = 24 # center-to-center: diameter (16) + gap (8) + Y_MAG = 11.2 def __init__(self): super().__init__() - self._opacity_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps) - self._opacity_target = 1.0 - self._hide_time = 0.0 - - def show_event(self): - self._opacity_target = 1.0 - self._hide_time = rl.get_time() + w = self.SPACING * 2 + self.RADIUS * 2 + h = self.RADIUS * 2 + int(self.Y_MAG) + self.set_rect(rl.Rectangle(0, 0, w, h)) def _render(self, _): - if rl.get_time() - self._hide_time > self.HIDE_TIME: - self._opacity_target = 0.0 - - self._opacity_filter.update(self._opacity_target) - - if self._opacity_filter.x < 0.01: - return - - cx = int(self._rect.x + self._rect.width / 2) - cy = int(self._rect.y + self._rect.height / 2) - - y_mag = 7 - anim_scale = 4 - spacing = 14 + # Balls rest at bottom center; bounce upward + base_x = int(self._rect.x + self._rect.width / 2) + base_y = int(self._rect.y + self._rect.height - self.RADIUS) for i in range(3): - x = cx - spacing + i * spacing - y = int(cy + min(math.sin((rl.get_time() - i * 0.2) * anim_scale) * y_mag, 0)) - alpha = int(np.interp(cy - y, [0, y_mag], [255 * 0.45, 255 * 0.9]) * self._opacity_filter.x) - rl.draw_circle(x, y, 5, rl.Color(255, 255, 255, alpha)) + x = base_x + (i - 1) * self.SPACING + y = int(base_y + min(math.sin((rl.get_time() - i * 0.2) * 4) * self.Y_MAG, 0)) + alpha = int(np.interp(base_y - y, [0, self.Y_MAG], [255 * 0.45, 255 * 0.9])) + rl.draw_circle(x, y, self.RADIUS, rl.Color(255, 255, 255, alpha)) class WifiIcon(Widget): @@ -270,11 +256,26 @@ class ForgetButton(Widget): rl.draw_texture_ex(self._trash_txt, (trash_x, trash_y), 0, 1.0, rl.WHITE) +class ScanningButton(BigButton): + def __init__(self): + super().__init__("", "searching for networks") + self.set_enabled(False) + self._loading_animation = LoadingAnimation() + + def _draw_content(self, btn_y: float): + super()._draw_content(btn_y) + anim = self._loading_animation + x = self._rect.x + self._rect.width - anim.rect.width - 40 + y = btn_y + self._rect.height - anim.rect.height - 30 + anim.set_position(x, y) + anim.render() + + class WifiUIMici(NavScroller): def __init__(self, wifi_manager: WifiManager): super().__init__() - self._loading_animation = LoadingAnimation() + self._scanning_btn = ScanningButton() self._wifi_manager = wifi_manager self._networks: dict[str, Network] = {} @@ -288,9 +289,9 @@ class WifiUIMici(NavScroller): def show_event(self): # Clear scroller items and update from latest scan results super().show_event() - self._loading_animation.show_event() self._wifi_manager.set_active(True) self._scroller.items.clear() + self._scroller.add_widget(self._scanning_btn) # trigger button update on latest sorted networks self._on_network_updated(self._wifi_manager.networks) @@ -310,6 +311,11 @@ class WifiUIMici(NavScroller): btn.set_click_callback(lambda ssid=network.ssid: self._connect_to_network(ssid)) self._scroller.add_widget(btn) + # Keep scanning button at the end + items = self._scroller.items + if self._scanning_btn in items: + items.append(items.pop(items.index(self._scanning_btn))) + # Mark networks no longer in scan results (display handled by _update_state) for btn in self._scroller.items: if isinstance(btn, WifiButton) and btn.network.ssid not in self._networks: @@ -371,16 +377,4 @@ class WifiUIMici(NavScroller): self._move_network_to_front(self._wifi_manager.wifi_state.ssid) - # Show loading animation near end - max_scroll = max(self._scroller.content_size - self._scroller.rect.width, 1) - progress = -self._scroller.scroll_panel.get_offset() / max_scroll - if progress > 0.8 or len(self._scroller.items) <= 1: - self._loading_animation.show_event() - def _render(self, _): - super()._render(self._rect) - - anim_w = 90 - anim_x = self._rect.x + self._rect.width - anim_w - anim_y = self._rect.y + self._rect.height - 25 + 2 - self._loading_animation.render(rl.Rectangle(anim_x, anim_y, anim_w, 20))