mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-12 12:52:13 +08:00
3aaf249236
* squash squash squash * scroller tici
559 lines
21 KiB
Python
559 lines
21 KiB
Python
import math
|
||
import numpy as np
|
||
import pyray as rl
|
||
from enum import IntEnum
|
||
from collections.abc import Callable
|
||
|
||
from openpilot.common.swaglog import cloudlog
|
||
from openpilot.system.ui.widgets.scroller import Scroller
|
||
from openpilot.system.ui.widgets.label import UnifiedLabel
|
||
from openpilot.selfdrive.ui.mici.widgets.button import BigButton, BigMultiToggle, BigToggle
|
||
from openpilot.selfdrive.ui.mici.widgets.dialog import BigMultiOptionDialog, BigInputDialog, BigDialogOptionButton, BigConfirmationDialogV2
|
||
from openpilot.system.ui.lib.application import gui_app, MousePos, FontWeight
|
||
from openpilot.system.ui.widgets import Widget, NavWidget
|
||
from openpilot.system.ui.lib.wifi_manager import WifiManager, Network, SecurityType, MeteredType
|
||
|
||
|
||
def normalize_ssid(ssid: str) -> str:
|
||
return ssid.replace("’", "'") # for iPhone hotspots
|
||
|
||
|
||
class NetworkPanelType(IntEnum):
|
||
NONE = 0
|
||
WIFI = 1
|
||
|
||
|
||
class LoadingAnimation(Widget):
|
||
def _render(self, _):
|
||
cx = int(self._rect.x + 70)
|
||
cy = int(self._rect.y + self._rect.height / 2 - 50)
|
||
|
||
y_mag = 20
|
||
anim_scale = 5
|
||
spacing = 28
|
||
|
||
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]))
|
||
rl.draw_circle(x, y, 10, rl.Color(255, 255, 255, alpha))
|
||
|
||
|
||
class WifiIcon(Widget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.set_rect(rl.Rectangle(0, 0, 89, 64))
|
||
|
||
self._wifi_slash_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_slash.png", 89, 64)
|
||
self._wifi_none_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_none.png", 89, 64)
|
||
self._wifi_low_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_low.png", 89, 64)
|
||
self._wifi_medium_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_medium.png", 89, 64)
|
||
self._wifi_full_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_full.png", 89, 64)
|
||
self._lock_txt = gui_app.texture("icons_mici/settings/network/new/lock.png", 23, 32)
|
||
|
||
self._network: Network | None = None
|
||
self._scale = 1.0
|
||
|
||
def set_current_network(self, network: Network):
|
||
self._network = network
|
||
|
||
def set_scale(self, scale: float):
|
||
self._scale = scale
|
||
|
||
def _render(self, _):
|
||
if self._network is None:
|
||
return
|
||
|
||
# Determine which wifi strength icon to use
|
||
strength = round(self._network.strength / 100 * 4)
|
||
if strength == 4:
|
||
strength_icon = self._wifi_full_txt
|
||
elif strength == 3:
|
||
strength_icon = self._wifi_medium_txt
|
||
elif strength == 2:
|
||
strength_icon = self._wifi_low_txt
|
||
elif self._network.strength < 0:
|
||
strength_icon = self._wifi_slash_txt
|
||
else:
|
||
strength_icon = self._wifi_none_txt
|
||
|
||
icon_x = int(self._rect.x + (self._rect.width - strength_icon.width * self._scale) // 2)
|
||
icon_y = int(self._rect.y + (self._rect.height - strength_icon.height * self._scale) // 2)
|
||
rl.draw_texture_ex(strength_icon, (icon_x, icon_y), 0.0, self._scale, rl.WHITE)
|
||
|
||
# Render lock icon at lower right of wifi icon if secured
|
||
if self._network.security_type not in (SecurityType.OPEN, SecurityType.UNSUPPORTED):
|
||
lock_scale = self._scale * 1.1
|
||
lock_x = int(icon_x + 1 + strength_icon.width * self._scale - self._lock_txt.width * lock_scale / 2)
|
||
lock_y = int(icon_y + 1 + strength_icon.height * self._scale - self._lock_txt.height * lock_scale / 2)
|
||
rl.draw_texture_ex(self._lock_txt, (lock_x, lock_y), 0.0, lock_scale, rl.WHITE)
|
||
|
||
|
||
class WifiItem(BigDialogOptionButton):
|
||
LEFT_MARGIN = 20
|
||
|
||
def __init__(self, network: Network):
|
||
super().__init__(network.ssid)
|
||
|
||
self.set_rect(rl.Rectangle(0, 0, gui_app.width, 64))
|
||
|
||
self._selected_txt = gui_app.texture("icons_mici/settings/network/new/wifi_selected.png", 48, 96)
|
||
|
||
self._network = network
|
||
self._wifi_icon = WifiIcon()
|
||
self._wifi_icon.set_current_network(network)
|
||
|
||
def set_current_network(self, network: Network):
|
||
self._network = network
|
||
self._wifi_icon.set_current_network(network)
|
||
|
||
def _render(self, _):
|
||
if self._network.is_connected:
|
||
selected_x = int(self._rect.x - self._selected_txt.width / 2)
|
||
selected_y = int(self._rect.y + (self._rect.height - self._selected_txt.height) / 2)
|
||
rl.draw_texture(self._selected_txt, selected_x, selected_y, rl.WHITE)
|
||
|
||
self._wifi_icon.set_scale((1.0 if self._selected else 0.65) * 0.7)
|
||
self._wifi_icon.render(rl.Rectangle(
|
||
self._rect.x + self.LEFT_MARGIN,
|
||
self._rect.y,
|
||
self._rect.height,
|
||
self._rect.height
|
||
))
|
||
|
||
if self._selected:
|
||
self._label.set_font_size(74)
|
||
self._label.set_color(rl.Color(255, 255, 255, int(255 * 0.9)))
|
||
self._label.set_font_weight(FontWeight.DISPLAY)
|
||
else:
|
||
self._label.set_font_size(70)
|
||
self._label.set_color(rl.Color(255, 255, 255, int(255 * 0.58)))
|
||
self._label.set_font_weight(FontWeight.DISPLAY_REGULAR)
|
||
|
||
label_offset = self.LEFT_MARGIN + self._wifi_icon.rect.width + 20
|
||
label_rect = rl.Rectangle(self._rect.x + label_offset, self._rect.y, self._rect.width - label_offset, self._rect.height)
|
||
self._label.set_text(normalize_ssid(self._network.ssid))
|
||
self._label.render(label_rect)
|
||
|
||
|
||
class ConnectButton(Widget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._bg_txt = gui_app.texture("icons_mici/settings/network/new/connect_button.png", 410, 100)
|
||
self._bg_pressed_txt = gui_app.texture("icons_mici/settings/network/new/connect_button_pressed.png", 410, 100)
|
||
self._bg_full_txt = gui_app.texture("icons_mici/settings/network/new/full_connect_button.png", 520, 100)
|
||
self._bg_full_pressed_txt = gui_app.texture("icons_mici/settings/network/new/full_connect_button_pressed.png", 520, 100)
|
||
|
||
self._full: bool = False
|
||
|
||
self._label = UnifiedLabel("", 36, FontWeight.MEDIUM, rl.Color(255, 255, 255, int(255 * 0.9)),
|
||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE)
|
||
|
||
@property
|
||
def full(self) -> bool:
|
||
return self._full
|
||
|
||
def set_full(self, full: bool):
|
||
self._full = full
|
||
self.set_rect(rl.Rectangle(0, 0, 520 if self._full else 410, 100))
|
||
|
||
def set_label(self, text: str):
|
||
self._label.set_text(text)
|
||
|
||
def _render(self, _):
|
||
if self._full:
|
||
bg_txt = self._bg_full_pressed_txt if self.is_pressed and self.enabled else self._bg_full_txt
|
||
else:
|
||
bg_txt = self._bg_pressed_txt if self.is_pressed and self.enabled else self._bg_txt
|
||
|
||
rl.draw_texture(bg_txt, int(self._rect.x), int(self._rect.y), rl.WHITE)
|
||
|
||
self._label.set_text_color(rl.Color(255, 255, 255, int(255 * 0.9) if self.enabled else int(255 * 0.9 * 0.65)))
|
||
self._label.render(self._rect)
|
||
|
||
|
||
class ForgetButton(Widget):
|
||
HORIZONTAL_MARGIN = 8
|
||
|
||
def __init__(self, forget_network: Callable, open_network_manage_page):
|
||
super().__init__()
|
||
self._forget_network = forget_network
|
||
self._open_network_manage_page = open_network_manage_page
|
||
|
||
self._bg_txt = gui_app.texture("icons_mici/settings/network/new/forget_button.png", 100, 100)
|
||
self._bg_pressed_txt = gui_app.texture("icons_mici/settings/network/new/forget_button_pressed.png", 100, 100)
|
||
self._trash_txt = gui_app.texture("icons_mici/settings/network/new/trash.png", 32, 36)
|
||
self.set_rect(rl.Rectangle(0, 0, 100 + self.HORIZONTAL_MARGIN * 2, 100))
|
||
|
||
def _handle_mouse_release(self, mouse_pos: MousePos):
|
||
super()._handle_mouse_release(mouse_pos)
|
||
dlg = BigConfirmationDialogV2("slide to forget", "icons_mici/settings/network/new/trash.png", red=True,
|
||
confirm_callback=self._forget_network)
|
||
gui_app.set_modal_overlay(dlg, callback=self._open_network_manage_page)
|
||
|
||
def _render(self, _):
|
||
bg_txt = self._bg_pressed_txt if self.is_pressed else self._bg_txt
|
||
rl.draw_texture(bg_txt, int(self._rect.x + self.HORIZONTAL_MARGIN), int(self._rect.y), rl.WHITE)
|
||
|
||
trash_x = int(self._rect.x + (self._rect.width - self._trash_txt.width) // 2)
|
||
trash_y = int(self._rect.y + (self._rect.height - self._trash_txt.height) // 2)
|
||
rl.draw_texture(self._trash_txt, trash_x, trash_y, rl.WHITE)
|
||
|
||
|
||
class NetworkInfoPage(NavWidget):
|
||
def __init__(self, wifi_manager, connect_callback: Callable, forget_callback: Callable, open_network_manage_page: Callable):
|
||
super().__init__()
|
||
self._wifi_manager = wifi_manager
|
||
|
||
self.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height))
|
||
|
||
self._wifi_icon = WifiIcon()
|
||
self._forget_btn = ForgetButton(lambda: forget_callback(self._network.ssid) if self._network is not None else None,
|
||
open_network_manage_page)
|
||
self._connect_btn = ConnectButton()
|
||
self._connect_btn.set_click_callback(lambda: connect_callback(self._network.ssid) if self._network is not None else None)
|
||
|
||
self._title = UnifiedLabel("", 64, FontWeight.DISPLAY, rl.Color(255, 255, 255, int(255 * 0.9)),
|
||
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE)
|
||
self._subtitle = UnifiedLabel("", 36, FontWeight.ROMAN, rl.Color(255, 255, 255, int(255 * 0.9 * 0.65)),
|
||
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE)
|
||
|
||
self.set_back_callback(lambda: gui_app.set_modal_overlay(None))
|
||
|
||
# State
|
||
self._network: Network | None = None
|
||
self._connecting: Callable[[], str | None] | None = None
|
||
|
||
def update_networks(self, networks: dict[str, Network]):
|
||
# update current network from latest scan results
|
||
for ssid, network in networks.items():
|
||
if self._network is not None and ssid == self._network.ssid:
|
||
self.set_current_network(network)
|
||
break
|
||
else:
|
||
# network disappeared, close page
|
||
gui_app.set_modal_overlay(None)
|
||
|
||
def _update_state(self):
|
||
super()._update_state()
|
||
# Modal overlays stop main UI rendering, so we need to call here
|
||
self._wifi_manager.process_callbacks()
|
||
|
||
if self._network is None:
|
||
return
|
||
|
||
self._connect_btn.set_full(not self._network.is_saved and not self._is_connecting)
|
||
if self._is_connecting:
|
||
self._connect_btn.set_label("connecting...")
|
||
self._connect_btn.set_enabled(False)
|
||
elif self._network.is_connected:
|
||
self._connect_btn.set_label("connected")
|
||
self._connect_btn.set_enabled(False)
|
||
elif self._network.security_type == SecurityType.UNSUPPORTED:
|
||
self._connect_btn.set_label("connect")
|
||
self._connect_btn.set_enabled(False)
|
||
else: # saved or unknown
|
||
self._connect_btn.set_label("connect")
|
||
self._connect_btn.set_enabled(True)
|
||
|
||
self._title.set_text(normalize_ssid(self._network.ssid))
|
||
if self._network.security_type == SecurityType.OPEN:
|
||
self._subtitle.set_text("open")
|
||
elif self._network.security_type == SecurityType.UNSUPPORTED:
|
||
self._subtitle.set_text("unsupported")
|
||
else:
|
||
self._subtitle.set_text("secured")
|
||
|
||
def set_current_network(self, network: Network):
|
||
self._network = network
|
||
self._wifi_icon.set_current_network(network)
|
||
|
||
def set_connecting(self, is_connecting: Callable[[], str | None]):
|
||
self._connecting = is_connecting
|
||
|
||
@property
|
||
def _is_connecting(self):
|
||
if self._connecting is None or self._network is None:
|
||
return False
|
||
is_connecting = self._connecting() == self._network.ssid
|
||
return is_connecting
|
||
|
||
def _render(self, _):
|
||
self._wifi_icon.render(rl.Rectangle(
|
||
self._rect.x + 32,
|
||
self._rect.y + (self._rect.height - self._connect_btn.rect.height - self._wifi_icon.rect.height) / 2,
|
||
self._wifi_icon.rect.width,
|
||
self._wifi_icon.rect.height,
|
||
))
|
||
|
||
self._title.render(rl.Rectangle(
|
||
self._rect.x + self._wifi_icon.rect.width + 32 + 32,
|
||
self._rect.y + 32 - 16,
|
||
self._rect.width - (self._wifi_icon.rect.width + 32 + 32),
|
||
64,
|
||
))
|
||
|
||
self._subtitle.render(rl.Rectangle(
|
||
self._rect.x + self._wifi_icon.rect.width + 32 + 32,
|
||
self._rect.y + 32 + 64 - 16,
|
||
self._rect.width - (self._wifi_icon.rect.width + 32 + 32),
|
||
48,
|
||
))
|
||
|
||
self._connect_btn.render(rl.Rectangle(
|
||
self._rect.x + 8,
|
||
self._rect.y + self._rect.height - self._connect_btn.rect.height,
|
||
self._connect_btn.rect.width,
|
||
self._connect_btn.rect.height,
|
||
))
|
||
|
||
if not self._connect_btn.full:
|
||
self._forget_btn.render(rl.Rectangle(
|
||
self._rect.x + self._rect.width - self._forget_btn.rect.width,
|
||
self._rect.y + self._rect.height - self._forget_btn.rect.height,
|
||
self._forget_btn.rect.width,
|
||
self._forget_btn.rect.height,
|
||
))
|
||
|
||
return -1
|
||
|
||
|
||
class WifiUIMici(BigMultiOptionDialog):
|
||
def __init__(self, wifi_manager: WifiManager, back_callback: Callable):
|
||
super().__init__([], None, None, right_btn_callback=None)
|
||
|
||
# Set up back navigation
|
||
self.set_back_callback(back_callback)
|
||
|
||
self._network_info_page = NetworkInfoPage(wifi_manager, self._connect_to_network, self._forget_network, self._open_network_manage_page)
|
||
self._network_info_page.set_connecting(lambda: self._connecting)
|
||
self._should_open_network_info_page = False # wait for scroll_to animation
|
||
|
||
self._loading_animation = LoadingAnimation()
|
||
|
||
self._wifi_manager = wifi_manager
|
||
self._connecting: str | None = None
|
||
self._networks: dict[str, Network] = {}
|
||
|
||
self._wifi_manager.add_callbacks(
|
||
need_auth=self._on_need_auth,
|
||
activated=self._on_activated,
|
||
forgotten=self._on_forgotten,
|
||
networks_updated=self._on_network_updated,
|
||
disconnected=self._on_disconnected,
|
||
)
|
||
|
||
def show_event(self):
|
||
# Call super to prepare scroller; selection scroll is handled dynamically
|
||
super().show_event()
|
||
self._wifi_manager.set_active(True)
|
||
self._scroller.show_event()
|
||
|
||
def hide_event(self):
|
||
super().hide_event()
|
||
self._wifi_manager.set_active(False)
|
||
|
||
def _update_state(self):
|
||
super()._update_state()
|
||
if self._should_open_network_info_page:
|
||
self._should_open_network_info_page = False
|
||
self._open_network_manage_page()
|
||
|
||
def _open_network_manage_page(self, result=None):
|
||
self._network_info_page.update_networks(self._networks)
|
||
gui_app.set_modal_overlay(self._network_info_page)
|
||
|
||
def _forget_network(self, ssid: str):
|
||
network = self._networks.get(ssid)
|
||
if network is None:
|
||
cloudlog.warning(f"Trying to forget unknown network: {ssid}")
|
||
return
|
||
|
||
self._wifi_manager.forget_connection(network.ssid)
|
||
|
||
def _on_network_updated(self, networks: list[Network]):
|
||
self._networks = {network.ssid: network for network in networks}
|
||
self._update_buttons()
|
||
self._network_info_page.update_networks(self._networks)
|
||
|
||
def _update_buttons(self):
|
||
for network in self._networks.values():
|
||
# pop and re-insert to eliminate stuttering on update (prevents position lost for a frame)
|
||
network_button_idx = next((i for i, btn in enumerate(self._scroller._items) if btn.option == network.ssid), None)
|
||
if network_button_idx is not None:
|
||
network_button = self._scroller._items.pop(network_button_idx)
|
||
# Update network on existing button
|
||
network_button.set_current_network(network)
|
||
else:
|
||
network_button = WifiItem(network)
|
||
|
||
def show_network_info_page(_network):
|
||
self._network_info_page.set_current_network(_network)
|
||
self._should_open_network_info_page = True
|
||
|
||
network_button.set_click_callback(lambda _net=network,_button=network_button: _button._selected and show_network_info_page(_net))
|
||
|
||
self.add_button(network_button)
|
||
|
||
# remove networks no longer present
|
||
self._scroller._items[:] = [btn for btn in self._scroller._items if btn.option in self._networks]
|
||
|
||
def _connect_with_password(self, ssid: str, password: str):
|
||
if password:
|
||
self._connecting = ssid
|
||
self._wifi_manager.connect_to_network(ssid, password)
|
||
self._update_buttons()
|
||
|
||
def _connect_to_network(self, ssid: str):
|
||
network = self._networks.get(ssid)
|
||
if network is None:
|
||
cloudlog.warning(f"Trying to connect to unknown network: {ssid}")
|
||
return
|
||
|
||
if network.is_saved:
|
||
self._connecting = network.ssid
|
||
self._wifi_manager.activate_connection(network.ssid)
|
||
self._update_buttons()
|
||
elif network.security_type == SecurityType.OPEN:
|
||
self._connecting = network.ssid
|
||
self._wifi_manager.connect_to_network(network.ssid, "")
|
||
self._update_buttons()
|
||
else:
|
||
self._on_need_auth(network.ssid, False)
|
||
|
||
def _on_need_auth(self, ssid, incorrect_password=True):
|
||
hint = "incorrect password..." if incorrect_password else "enter password..."
|
||
dlg = BigInputDialog(hint, "", minimum_length=8,
|
||
confirm_callback=lambda _password: self._connect_with_password(ssid, _password))
|
||
# go back to the manage network page
|
||
gui_app.set_modal_overlay(dlg, self._open_network_manage_page)
|
||
|
||
def _on_activated(self):
|
||
self._connecting = None
|
||
|
||
def _on_forgotten(self):
|
||
self._connecting = None
|
||
|
||
def _on_disconnected(self):
|
||
self._connecting = None
|
||
|
||
def _render(self, _):
|
||
super()._render(_)
|
||
|
||
if not self._networks:
|
||
self._loading_animation.render(self._rect)
|
||
|
||
|
||
class NetworkLayoutMici(NavWidget):
|
||
def __init__(self, back_callback: Callable):
|
||
super().__init__()
|
||
|
||
self._current_panel = NetworkPanelType.WIFI
|
||
self.set_back_enabled(lambda: self._current_panel == NetworkPanelType.NONE)
|
||
|
||
self._wifi_manager = WifiManager()
|
||
self._wifi_manager.set_active(False)
|
||
self._wifi_ui = WifiUIMici(self._wifi_manager, back_callback=lambda: self._switch_to_panel(NetworkPanelType.NONE))
|
||
|
||
self._wifi_manager.add_callbacks(
|
||
networks_updated=self._on_network_updated,
|
||
)
|
||
|
||
_tethering_icon = "icons_mici/settings/network/tethering.png"
|
||
|
||
# ******** Tethering ********
|
||
def tethering_toggle_callback(checked: bool):
|
||
self._tethering_toggle_btn.set_enabled(False)
|
||
self._network_metered_btn.set_enabled(False)
|
||
self._wifi_manager.set_tethering_active(checked)
|
||
|
||
self._tethering_toggle_btn = BigToggle("enable tethering", "", toggle_callback=tethering_toggle_callback)
|
||
|
||
def tethering_password_callback(password: str):
|
||
if password:
|
||
self._wifi_manager.set_tethering_password(password)
|
||
|
||
def tethering_password_clicked():
|
||
tethering_password = self._wifi_manager.tethering_password
|
||
dlg = BigInputDialog("enter password...", tethering_password, minimum_length=8,
|
||
confirm_callback=tethering_password_callback)
|
||
gui_app.set_modal_overlay(dlg)
|
||
|
||
txt_tethering = gui_app.texture(_tethering_icon, 64, 53)
|
||
self._tethering_password_btn = BigButton("tethering password", "", txt_tethering)
|
||
self._tethering_password_btn.set_click_callback(tethering_password_clicked)
|
||
|
||
# ******** IP Address ********
|
||
self._ip_address_btn = BigButton("IP Address", "Not connected")
|
||
|
||
# ******** Network Metered ********
|
||
def network_metered_callback(value: str):
|
||
self._network_metered_btn.set_enabled(False)
|
||
metered = {
|
||
'default': MeteredType.UNKNOWN,
|
||
'metered': MeteredType.YES,
|
||
'unmetered': MeteredType.NO
|
||
}.get(value, MeteredType.UNKNOWN)
|
||
self._wifi_manager.set_current_network_metered(metered)
|
||
|
||
# TODO: signal for current network metered type when changing networks, this is wrong until you press it once
|
||
# TODO: disable when not connected
|
||
self._network_metered_btn = BigMultiToggle("network usage", ["default", "metered", "unmetered"], select_callback=network_metered_callback)
|
||
self._network_metered_btn.set_enabled(False)
|
||
|
||
wifi_button = BigButton("wi-fi")
|
||
wifi_button.set_click_callback(lambda: self._switch_to_panel(NetworkPanelType.WIFI))
|
||
|
||
# Main scroller ----------------------------------
|
||
self._scroller = Scroller([
|
||
wifi_button,
|
||
self._network_metered_btn,
|
||
self._tethering_toggle_btn,
|
||
self._tethering_password_btn,
|
||
self._ip_address_btn,
|
||
], snap_items=False)
|
||
|
||
# Set up back navigation
|
||
self.set_back_callback(back_callback)
|
||
|
||
def show_event(self):
|
||
super().show_event()
|
||
self._current_panel = NetworkPanelType.NONE
|
||
self._wifi_ui.show_event()
|
||
self._scroller.show_event()
|
||
|
||
def hide_event(self):
|
||
super().hide_event()
|
||
self._wifi_ui.hide_event()
|
||
|
||
def _on_network_updated(self, networks: list[Network]):
|
||
# Update tethering state
|
||
tethering_active = self._wifi_manager.is_tethering_active()
|
||
# TODO: use real signals (like activated/settings changed, etc.) to speed up re-enabling buttons
|
||
self._tethering_toggle_btn.set_enabled(True)
|
||
self._network_metered_btn.set_enabled(lambda: not tethering_active and bool(self._wifi_manager.ipv4_address))
|
||
self._tethering_toggle_btn.set_checked(tethering_active)
|
||
|
||
# Update IP address
|
||
self._ip_address_btn.set_value(self._wifi_manager.ipv4_address or "Not connected")
|
||
|
||
# Update network metered
|
||
self._network_metered_btn.set_value(
|
||
{
|
||
MeteredType.UNKNOWN: 'default',
|
||
MeteredType.YES: 'metered',
|
||
MeteredType.NO: 'unmetered'
|
||
}.get(self._wifi_manager.current_network_metered, 'default'))
|
||
|
||
def _switch_to_panel(self, panel_type: NetworkPanelType):
|
||
self._current_panel = panel_type
|
||
|
||
def _render(self, rect: rl.Rectangle):
|
||
self._wifi_manager.process_callbacks()
|
||
|
||
if self._current_panel == NetworkPanelType.WIFI:
|
||
self._wifi_ui.render(rect)
|
||
else:
|
||
self._scroller.render(rect)
|