WifiUi: add new networks to end, delete buttons on exit (#37189)

* add networks to end, remove bad scroller restore logic that sometimes starts in the middle

* works

* almost

* wifi slash

* clean up

* clean up

* opactiy

* more clean up

* more clean up

* set enabled and network missing on regain network

* cmt
This commit is contained in:
Shane Smiskol
2026-02-12 16:23:58 -08:00
committed by GitHub
parent 1257d31a56
commit eb5cd542d9

View File

@@ -36,20 +36,29 @@ class WifiIcon(Widget):
super().__init__()
self.set_rect(rl.Rectangle(0, 0, 86, 64))
self._wifi_slash_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_slash.png", 86, 64)
self._wifi_low_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_low.png", 86, 64)
self._wifi_medium_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_medium.png", 86, 64)
self._wifi_full_txt = gui_app.texture("icons_mici/settings/network/wifi_strength_full.png", 86, 64)
self._lock_txt = gui_app.texture("icons_mici/settings/network/new/lock.png", 22, 32)
self._network: Network | None = None
self._network_missing = False # if network disappeared from scan results
self._scale = 1.0
self._opacity = 1.0
def set_current_network(self, network: Network):
self._network = network
def set_network_missing(self, missing: bool):
self._network_missing = missing
def set_scale(self, scale: float):
self._scale = scale
def set_opacity(self, opacity: float):
self._opacity = opacity
@staticmethod
def get_strength_icon_idx(strength: int) -> int:
return round(strength / 100 * 2)
@@ -60,23 +69,26 @@ class WifiIcon(Widget):
# Determine which wifi strength icon to use
strength = self.get_strength_icon_idx(self._network.strength)
if strength == 2:
if self._network_missing:
strength_icon = self._wifi_slash_txt
elif strength == 2:
strength_icon = self._wifi_full_txt
elif strength == 1:
strength_icon = self._wifi_medium_txt
else:
strength_icon = self._wifi_low_txt
tint = rl.Color(255, 255, 255, int(255 * self._opacity))
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)
rl.draw_texture_ex(strength_icon, (icon_x, icon_y), 0.0, self._scale, tint)
# 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)
rl.draw_texture_ex(self._lock_txt, (lock_x, lock_y), 0.0, lock_scale, tint)
class WifiItem(BigDialogOptionButton):
@@ -93,16 +105,26 @@ class WifiItem(BigDialogOptionButton):
self._wifi_icon = WifiIcon()
self._wifi_icon.set_current_network(network)
def set_network_missing(self, missing: bool):
self._wifi_icon.set_network_missing(missing)
def set_current_network(self, network: Network):
self._network = network
self._wifi_icon.set_current_network(network)
# reset if we see the network again
self.set_enabled(True)
self.set_network_missing(False)
def _render(self, _):
disabled_alpha = 0.35 if not self.enabled else 1.0
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_opacity(disabled_alpha)
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,
@@ -113,11 +135,11 @@ class WifiItem(BigDialogOptionButton):
if self._selected:
self._label.set_font_size(self.SELECTED_HEIGHT)
self._label.set_color(rl.Color(255, 255, 255, int(255 * 0.9)))
self._label.set_color(rl.Color(255, 255, 255, int(255 * 0.9 * disabled_alpha)))
self._label.set_font_weight(FontWeight.DISPLAY)
else:
self._label.set_font_size(self.HEIGHT)
self._label.set_color(rl.Color(255, 255, 255, int(255 * 0.58)))
self._label.set_color(rl.Color(255, 255, 255, int(255 * 0.58 * disabled_alpha)))
self._label.set_font_weight(FontWeight.DISPLAY_REGULAR)
label_offset = self.LEFT_MARGIN + self._wifi_icon.rect.width + 20
@@ -314,9 +336,6 @@ class NetworkInfoPage(NavWidget):
class WifiUIMici(BigMultiOptionDialog):
# Wait this long after user interacts with widget to update network list
INACTIVITY_TIMEOUT = 1
def __init__(self, wifi_manager: WifiManager, back_callback: Callable):
super().__init__([], None)
@@ -332,10 +351,6 @@ class WifiUIMici(BigMultiOptionDialog):
self._connecting: str | None = None
self._networks: dict[str, Network] = {}
# widget state
self._last_interaction_time = -float('inf')
self._restore_selection = False
self._wifi_manager.add_callbacks(
need_auth=self._on_need_auth,
activated=self._on_activated,
@@ -348,11 +363,12 @@ class WifiUIMici(BigMultiOptionDialog):
# Call super to prepare scroller; selection scroll is handled dynamically
super().show_event()
self._wifi_manager.set_active(True)
self._last_interaction_time = -float('inf')
def hide_event(self):
super().hide_event()
self._wifi_manager.set_active(False)
# clear scroller items to remove old networks on next show
self._scroller._items.clear()
def _open_network_manage_page(self, result=None):
self._network_info_page.update_networks(self._networks)
@@ -372,27 +388,28 @@ class WifiUIMici(BigMultiOptionDialog):
self._network_info_page.update_networks(self._networks)
def _update_buttons(self):
# Don't update buttons while user is actively interacting
if rl.get_time() - self._last_interaction_time < self.INACTIVITY_TIMEOUT:
return
# Only add new buttons to the end. Update existing buttons without re-sorting so user can freely scroll around
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)
self._scroller._items[network_button_idx].set_current_network(network)
else:
network_button = WifiItem(network)
self._scroller.add_widget(network_button)
self._scroller.add_widget(network_button)
# Move connected network to the start
connected_btn_idx = next((i for i, btn in enumerate(self._scroller._items) if btn._network.is_connected), None)
if connected_btn_idx is not None and connected_btn_idx > 0:
self._scroller._items.insert(0, self._scroller._items.pop(connected_btn_idx))
self._scroller._layout() # fixes selected style single frame stutter
# remove networks no longer present
self._scroller._items[:] = [btn for btn in self._scroller._items if btn.option in self._networks]
# try to restore previous selection to prevent jumping from adding/removing/reordering buttons
self._restore_selection = True
# Disable networks no longer present
for btn in self._scroller._items:
if btn.option not in self._networks:
btn.set_enabled(False)
btn.set_network_missing(True)
def _connect_with_password(self, ssid: str, password: str):
if password:
@@ -440,19 +457,7 @@ class WifiUIMici(BigMultiOptionDialog):
def _on_disconnected(self):
self._connecting = None
def _update_state(self):
super()._update_state()
if self.is_pressed:
self._last_interaction_time = rl.get_time()
def _render(self, _):
# Update Scroller layout and restore current selection whenever buttons are updated, before first render
current_selection = self.get_selected_option()
if self._restore_selection and current_selection in self._networks:
self._scroller._layout()
BigMultiOptionDialog._on_option_selected(self, current_selection)
self._restore_selection = None
super()._render(_)
if not self._networks: