import gi from gi.repository import Gdk, GLib from fabric.widgets.box import Box from fabric.widgets.button import Button from fabric.widgets.entry import Entry from fabric.widgets.image import Image from fabric.widgets.label import Label # from widgets.wayland import WaylandWindow as Window from fabric.widgets.window import Window gi.require_version("Gtk", "3.0") class WiFiPasswordDialog(Window): def __init__( self, ssid: str, on_connect_callback=None, on_cancel_callback=None, on_dialog_closed=None, **kwargs, ): super().__init__( title="modus", layer="overlay", anchor="center", keyboard_mode="on-demand", visible=False, name="wifi-password-dialog", **kwargs, ) self.ssid = ssid self.on_connect_callback = on_connect_callback self.on_cancel_callback = on_cancel_callback # self.set_size_request(400, 300) self.set_resizable(False) self.on_dialog_closed = on_dialog_closed self.is_connecting = False self.connection_timeout_id = None self._create_dialog_content() self.connect("key-press-event", self._on_key_press) self.connect("notify::visible", self._on_visibility_changed) def _create_dialog_content(self): self.wifi_icon = Image( icon_name="network-wireless-symbolic", size=20, name="wifi-dialog-icon" ) self.title_label = Label( label=f'The Wi-Fi network "{self.ssid}" requires a WPA2 password.', name="wifi-dialog-title", h_align="start", wrap=True, max_width_chars=40, ) self.title_container = Box( orientation="h", spacing=8, children=[self.wifi_icon, self.title_label], name="wifi-dialog-title-container", h_align="start", ) self.error_label = Label( label="Incorrect password. Please try again.", name="wifi-dialog-error", h_align="center", visible=False, ) self.password_label = Label( label="Password:", name="wifi-dialog-password-label", h_align="start" ) self.password_entry = Entry( placeholder_text="Enter password", name="wifi-dialog-password-entry", visibility=False, h_expand=True, h_align="fill", ) self.password_entry.connect("activate", lambda *_: self._on_join_clicked()) self.password_entry.connect("changed", self._on_password_changed) self.password_visible = False self.show_password_button = Button( image=Image(icon_name="view-conceal-symbolic", size=16), name="wifi-dialog-show-password-button", on_clicked=self._on_show_password_clicked, ) self.show_password_label = Label( label="Show password", name="wifi-dialog-show-password-label" ) self.show_password_box = Box( orientation="h", spacing=8, children=[self.show_password_button, self.show_password_label], name="wifi-dialog-show-password-box", ) self.cancel_button = Button( label="Cancel", name="wifi-dialog-cancel-button", on_clicked=self._on_cancel_clicked, ) self.join_button = Button( label="Join", name="wifi-dialog-join-button", on_clicked=self._on_join_clicked, ) self.button_box = Box( orientation="h", spacing=12, h_expand=True, children=[self.cancel_button, self.join_button], name="wifi-dialog-button-box", h_align="end", ) self.password_container = Box( orientation="v", h_expand=True, spacing=6, children=[self.password_label, self.password_entry, self.show_password_box], name="wifi-dialog-password-container", ) self.content_box = Box( orientation="v", h_expand=True, spacing=12, children=[ self.title_container, self.error_label, self.password_container, self.button_box, ], name="wifi-dialog-content", h_align="fill", v_align="center", ) self.dialog_background = Box( children=[self.content_box], name="wifi-dialog-background", h_align="center", v_align="center", ) self.children = self.dialog_background self._update_join_button_state() def _on_password_changed(self, entry): self._update_join_button_state() def _update_join_button_state(self): password = self.password_entry.get_text().strip() has_password = len(password) > 0 if has_password: self.join_button.set_opacity(1.0) self.join_button.remove_style_class("disabled") else: self.join_button.set_opacity(0.5) self.join_button.add_style_class("disabled") def _on_show_password_clicked(self, *args): self.password_visible = not self.password_visible self.password_entry.set_visibility(self.password_visible) icon_name = ( "view-reveal-symbolic" if self.password_visible else "view-conceal-symbolic" ) self.show_password_button.get_image().set_property("icon-name", icon_name) def _on_key_press(self, widget, event): keyval = event.keyval if keyval == Gdk.KEY_Return or keyval == Gdk.KEY_KP_Enter: self._on_join_clicked() return True elif keyval == Gdk.KEY_Escape: self._on_cancel_clicked() return True return False def _on_visibility_changed(self, widget, *args): """Handle visibility changes""" if self.get_visible(): GLib.timeout_add(100, lambda: self.password_entry.grab_focus()) def _on_cancel_clicked(self, *args): self.hide() if self.on_cancel_callback: self.on_cancel_callback() if self.on_dialog_closed: self.on_dialog_closed() def _on_join_clicked(self, *args): if self.is_connecting: return password = self.password_entry.get_text().strip() if not password: self.password_entry.grab_focus() return self.is_connecting = True self.join_button.set_sensitive(False) self.connection_timeout_id = GLib.timeout_add(5000, self._connection_timeout) self.error_label.set_visible(False) self.hide() if self.on_connect_callback: self.on_connect_callback(self.ssid, password) if self.on_dialog_closed: self.on_dialog_closed() def _connection_timeout(self): if self.is_connecting: self.is_connecting = False self.join_button.set_sensitive(True) self.show_error("Connection timeout. Please try again.") return False def show_dialog(self): if self.connection_timeout_id: GLib.source_remove(self.connection_timeout_id) self.connection_timeout_id = None self.show_all() self.password_entry.set_text("") self.error_label.set_visible(False) self.password_visible = False self.password_entry.set_visibility(False) self.show_password_button.get_image().set_property( "icon-name", "view-conceal-symbolic" ) self.is_connecting = False self.join_button.set_sensitive(True) self._update_join_button_state() def show_error(self, message="Incorrect password. Please try again."): if self.connection_timeout_id: GLib.source_remove(self.connection_timeout_id) self.connection_timeout_id = None self.is_connecting = False self.join_button.set_sensitive(True) if not self.get_visible(): self.error_label.set_text(message) self.error_label.set_visible(True) self.show_all() GLib.timeout_add(10, lambda: self._focus_and_select_password()) else: self.error_label.set_text(message) self.error_label.set_visible(True) self._focus_and_select_password() def _focus_and_select_password(self): try: self.password_entry.grab_focus() self.password_entry.select_region(0, -1) return False except: return False def get_password(self): return self.password_entry.get_text() def destroy_dialog(self): self.hide() self.destroy()