🐛 fix(ui): prevent window resizing in settings app
This commit is contained in:
6 files changed
+279
-258
No files matched your search
@@ -1,12 +1,13 @@
|
||||
import os
|
||||
|
||||
import gi
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.grid import Grid
|
||||
from fabric.widgets.image import Image
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.scale import Scale
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from gi.repository import GdkPixbuf, Gtk
|
||||
|
||||
from config.data import NOTIF_POS_DEFAULT, NOTIF_POS_KEY
|
||||
@@ -22,7 +23,7 @@ class AppearanceTab:
|
||||
self.themes = themes
|
||||
self.parent_window = parent_window
|
||||
self.selected_face_icon = None
|
||||
|
||||
|
||||
# Widget references
|
||||
self.wall_dir_chooser = None
|
||||
self.face_image = None
|
||||
@@ -60,7 +61,7 @@ class AppearanceTab:
|
||||
|
||||
# Create wallpaper and profile icon section
|
||||
self._create_wallpaper_profile_section(vbox)
|
||||
|
||||
|
||||
# Add separator
|
||||
separator1 = Box(
|
||||
style="min-height: 1px; background-color: alpha(@fg_color, 0.2); margin: 5px 0px;",
|
||||
@@ -85,11 +86,7 @@ class AppearanceTab:
|
||||
|
||||
def _create_wallpaper_profile_section(self, vbox):
|
||||
"""Create wallpaper and profile icon section"""
|
||||
top_grid = Grid(
|
||||
column_spacing=20,
|
||||
row_spacing=5,
|
||||
style="margin-bottom: 10px;"
|
||||
)
|
||||
top_grid = Grid(column_spacing=20, row_spacing=5, style="margin-bottom: 10px;")
|
||||
vbox.add(top_grid)
|
||||
|
||||
# Wallpaper section
|
||||
@@ -99,9 +96,7 @@ class AppearanceTab:
|
||||
top_grid.attach(wall_label, 0, 1, 1, 1)
|
||||
|
||||
chooser_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.wall_dir_chooser = Gtk.FileChooserButton(
|
||||
title="Select a folder", action=Gtk.FileChooserAction.SELECT_FOLDER
|
||||
@@ -135,9 +130,7 @@ class AppearanceTab:
|
||||
top_grid.attach(face_image_container, 2, 1, 1, 1)
|
||||
|
||||
browse_btn_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
face_btn = Button(
|
||||
label="Browse...",
|
||||
@@ -156,7 +149,7 @@ class AppearanceTab:
|
||||
layout_grid = Grid(
|
||||
column_spacing=20,
|
||||
row_spacing=10,
|
||||
style="margin-left: 10px; margin-top: 5px;"
|
||||
style="margin-left: 10px; margin-top: 5px;",
|
||||
)
|
||||
vbox.add(layout_grid)
|
||||
|
||||
@@ -165,9 +158,7 @@ class AppearanceTab:
|
||||
layout_grid.attach(ws_mode_label, 0, 0, 1, 1)
|
||||
|
||||
ws_mode_combo_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.ws_mode_combo = Gtk.ComboBoxText()
|
||||
self.ws_mode_combo.set_tooltip_text("Select how workspaces are displayed")
|
||||
@@ -200,9 +191,7 @@ class AppearanceTab:
|
||||
)
|
||||
layout_grid.attach(ws_chinese_label, 2, 0, 1, 1)
|
||||
ws_chinese_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.ws_chinese_switch = Gtk.Switch(
|
||||
active=bind_vars.get("workspace_use_chinese_numerals", False),
|
||||
@@ -218,9 +207,7 @@ class AppearanceTab:
|
||||
position_label = Label(label="Dock Position", h_align="start", v_align="center")
|
||||
layout_grid.attach(position_label, 0, 1, 1, 1)
|
||||
position_combo_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.position_combo = Gtk.ComboBoxText()
|
||||
self.position_combo.set_tooltip_text("Select the position of the dock")
|
||||
@@ -240,9 +227,7 @@ class AppearanceTab:
|
||||
dock_theme_label = Label(label="Dock Theme", h_align="start", v_align="center")
|
||||
layout_grid.attach(dock_theme_label, 2, 1, 1, 1)
|
||||
dock_theme_combo_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.dock_theme_combo = Gtk.ComboBoxText()
|
||||
self.dock_theme_combo.set_tooltip_text("Select the visual theme for the dock")
|
||||
@@ -260,9 +245,7 @@ class AppearanceTab:
|
||||
dock_label = Label(label="Show Dock", h_align="start", v_align="center")
|
||||
layout_grid.attach(dock_label, 0, 2, 1, 1)
|
||||
dock_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.dock_switch = Gtk.Switch(active=bind_vars.get("dock_enabled", True))
|
||||
self.dock_switch.connect("notify::active", self.on_dock_enabled_changed)
|
||||
@@ -275,9 +258,7 @@ class AppearanceTab:
|
||||
)
|
||||
layout_grid.attach(dock_hover_label, 2, 2, 1, 1)
|
||||
dock_hover_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.dock_hover_switch = Gtk.Switch(
|
||||
active=bind_vars.get("dock_always_occluded", False),
|
||||
@@ -292,9 +273,7 @@ class AppearanceTab:
|
||||
)
|
||||
layout_grid.attach(dock_auto_hide_label, 0, 3, 1, 1)
|
||||
dock_auto_hide_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.dock_auto_hide_switch = Gtk.Switch(
|
||||
active=bind_vars.get("dock_auto_hide", False),
|
||||
@@ -309,9 +288,7 @@ class AppearanceTab:
|
||||
)
|
||||
layout_grid.attach(dock_hide_special_label, 2, 3, 1, 1)
|
||||
dock_hide_special_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.dock_hide_special_switch = Gtk.Switch(
|
||||
active=bind_vars.get("dock_hide_special_workspace", True),
|
||||
@@ -351,7 +328,7 @@ class AppearanceTab:
|
||||
self.application_switcher_scale = Scale(
|
||||
min_value=5,
|
||||
max_value=20,
|
||||
value=bind_vars.get("window_switcher_items_per_row",13),
|
||||
value=bind_vars.get("window_switcher_items_per_row", 13),
|
||||
increments=(2, 4),
|
||||
draw_value=True,
|
||||
value_position="right",
|
||||
@@ -361,7 +338,9 @@ class AppearanceTab:
|
||||
self.application_switcher_scale.set_tooltip_text(
|
||||
"Adjust the number of items per row in the application switcher"
|
||||
)
|
||||
layout_grid.attach(self.application_switcher_scale, 1, 5, 3, 1) # Span 3 columns
|
||||
layout_grid.attach(
|
||||
self.application_switcher_scale, 1, 5, 3, 1
|
||||
) # Span 3 columns
|
||||
|
||||
# Hide Special Workspace Apps
|
||||
dock_hide_special_apps_label = Label(
|
||||
@@ -369,9 +348,7 @@ class AppearanceTab:
|
||||
)
|
||||
layout_grid.attach(dock_hide_special_apps_label, 0, 6, 1, 1)
|
||||
dock_hide_special_apps_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.dock_hide_special_apps_switch = Gtk.Switch(
|
||||
active=bind_vars.get("dock_hide_special_workspace_apps", True),
|
||||
@@ -390,9 +367,7 @@ class AppearanceTab:
|
||||
layout_grid.attach(notification_pos_label, 2, 6, 1, 1)
|
||||
|
||||
notification_pos_combo_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
|
||||
self.notification_pos_combo = Gtk.ComboBoxText()
|
||||
@@ -426,7 +401,7 @@ class AppearanceTab:
|
||||
components_grid = Grid(
|
||||
column_spacing=15,
|
||||
row_spacing=8,
|
||||
style="margin-left: 10px; margin-top: 5px;"
|
||||
style="margin-left: 10px; margin-top: 5px;",
|
||||
)
|
||||
vbox.add(components_grid)
|
||||
|
||||
@@ -452,9 +427,7 @@ class AppearanceTab:
|
||||
)
|
||||
components_grid.attach(corners_label, 0, 0, 1, 1)
|
||||
switch_container_corners = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
switch_container_corners.add(self.corners_switch)
|
||||
components_grid.attach(switch_container_corners, 1, 0, 1, 1)
|
||||
@@ -472,9 +445,7 @@ class AppearanceTab:
|
||||
components_grid.attach(component_label, col, row, 1, 1)
|
||||
|
||||
switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
component_switch = Gtk.Switch(
|
||||
active=bind_vars.get(f"dock_{name}_visible", True)
|
||||
@@ -568,10 +539,16 @@ class AppearanceTab:
|
||||
values["dock_enabled"] = self.dock_switch.get_active()
|
||||
values["dock_auto_hide"] = self.dock_auto_hide_switch.get_active()
|
||||
values["dock_always_occluded"] = self.dock_hover_switch.get_active()
|
||||
values["dock_hide_special_workspace"] = self.dock_hide_special_switch.get_active()
|
||||
values["dock_hide_special_workspace_apps"] = self.dock_hide_special_apps_switch.get_active()
|
||||
values["dock_hide_special_workspace"] = (
|
||||
self.dock_hide_special_switch.get_active()
|
||||
)
|
||||
values["dock_hide_special_workspace_apps"] = (
|
||||
self.dock_hide_special_apps_switch.get_active()
|
||||
)
|
||||
values["dock_icon_size"] = int(self.icon_size_scale.get_value())
|
||||
values["window_switcher_items_per_row"] = int(self.application_switcher_scale.get_value())
|
||||
values["window_switcher_items_per_row"] = int(
|
||||
self.application_switcher_scale.get_value()
|
||||
)
|
||||
values["corners_visible"] = self.corners_switch.get_active()
|
||||
values["dock_theme"] = self.dock_theme_combo.get_active_text()
|
||||
|
||||
@@ -615,8 +592,12 @@ class AppearanceTab:
|
||||
self.dock_switch.set_active(defaults.get("dock_enabled", True))
|
||||
self.dock_auto_hide_switch.set_active(defaults.get("dock_auto_hide", False))
|
||||
self.dock_hover_switch.set_active(defaults.get("dock_always_occluded", False))
|
||||
self.dock_hide_special_switch.set_active(defaults.get("dock_hide_special_workspace", True))
|
||||
self.dock_hide_special_apps_switch.set_active(defaults.get("dock_hide_special_workspace_apps", True))
|
||||
self.dock_hide_special_switch.set_active(
|
||||
defaults.get("dock_hide_special_workspace", True)
|
||||
)
|
||||
self.dock_hide_special_apps_switch.set_active(
|
||||
defaults.get("dock_hide_special_workspace_apps", True)
|
||||
)
|
||||
|
||||
# Update sensitivity based on dock enabled state
|
||||
dock_enabled = self.dock_switch.get_active()
|
||||
@@ -659,7 +640,9 @@ class AppearanceTab:
|
||||
except ValueError:
|
||||
self.ws_mode_combo.set_active(0)
|
||||
|
||||
self.ws_chinese_switch.set_active(defaults.get("workspace_use_chinese_numerals", False))
|
||||
self.ws_chinese_switch.set_active(
|
||||
defaults.get("workspace_use_chinese_numerals", False)
|
||||
)
|
||||
self.ws_chinese_switch.set_sensitive(default_nums)
|
||||
self.corners_switch.set_active(defaults.get("corners_visible", True))
|
||||
|
||||
@@ -679,4 +662,6 @@ class AppearanceTab:
|
||||
|
||||
# Reset scales
|
||||
self.icon_size_scale.set_value(defaults.get("dock_icon_size", 20))
|
||||
self.application_switcher_scale.set_value(defaults.get("window_switcher_items_per_row", 13))
|
||||
self.application_switcher_scale.set_value(
|
||||
defaults.get("window_switcher_items_per_row", 13)
|
||||
)
|
||||
@@ -1,3 +1,5 @@
|
||||
import subprocess
|
||||
|
||||
from fabric.bluetooth import BluetoothClient, BluetoothDevice
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
@@ -6,7 +8,6 @@ from fabric.widgets.image import Image
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from gi.repository import GLib, Gtk
|
||||
import subprocess
|
||||
|
||||
import utils.icons as icons
|
||||
|
||||
@@ -20,18 +21,20 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
"notify::closed", lambda *_: self.device.closed and self.destroy()
|
||||
)
|
||||
|
||||
self.connection_label = Label(markup=icons.bluetooth_disconnected, style="font-size: 22px;")
|
||||
self.connection_label = Label(
|
||||
markup=icons.bluetooth_disconnected, style="font-size: 22px;"
|
||||
)
|
||||
self.connect_button = Button(
|
||||
label="Connect",
|
||||
on_clicked=lambda *_: self._handle_device_action(),
|
||||
)
|
||||
|
||||
|
||||
# Create device name label that we can update
|
||||
self.device_name_label = Label(
|
||||
label=device.alias or device.name,
|
||||
h_expand=True,
|
||||
h_align="start",
|
||||
ellipsization="end"
|
||||
ellipsization="end",
|
||||
)
|
||||
|
||||
self.start_children = [
|
||||
@@ -50,8 +53,6 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
|
||||
self.device.emit("changed")
|
||||
|
||||
|
||||
|
||||
def _handle_device_action(self):
|
||||
"""Handle connect/disconnect button click"""
|
||||
if self.device.connected:
|
||||
@@ -71,23 +72,31 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
device_address = self.device.address
|
||||
|
||||
# Show pairing notification
|
||||
subprocess.run([
|
||||
"notify-send",
|
||||
"Bluetooth Pairing",
|
||||
f"Attempting to pair with {device_name}...",
|
||||
"--icon=bluetooth",
|
||||
"--urgency=normal",
|
||||
"--app-name=Bluetooth"
|
||||
], check=False)
|
||||
subprocess.run(
|
||||
[
|
||||
"notify-send",
|
||||
"Bluetooth Pairing",
|
||||
f"Attempting to pair with {device_name}...",
|
||||
"--icon=bluetooth",
|
||||
"--urgency=normal",
|
||||
"--app-name=Bluetooth",
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
|
||||
# Use bluetoothctl to pair the device
|
||||
# This will trigger the system's pairing agent which should show the pairing dialog
|
||||
pairing_process = subprocess.Popen([
|
||||
"bluetoothctl", "pair", device_address
|
||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
pairing_process = subprocess.Popen(
|
||||
["bluetoothctl", "pair", device_address],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
|
||||
# Monitor the pairing process in a separate thread
|
||||
GLib.timeout_add(100, self._monitor_pairing_process, pairing_process, device_name)
|
||||
GLib.timeout_add(
|
||||
100, self._monitor_pairing_process, pairing_process, device_name
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to start pairing process: {e}")
|
||||
@@ -121,7 +130,7 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
subprocess.run(["bluetoothctl", "trust", device_address], check=False)
|
||||
|
||||
# Connect to the device
|
||||
GLib.timeout_add(1000, lambda: setattr(self.device, 'connected', True))
|
||||
GLib.timeout_add(1000, lambda: setattr(self.device, "connected", True))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to trust/connect device: {e}")
|
||||
@@ -130,14 +139,17 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
"""Show notification when pairing succeeds"""
|
||||
try:
|
||||
device_name = self.device.alias or self.device.name
|
||||
subprocess.run([
|
||||
"notify-send",
|
||||
"Bluetooth Paired",
|
||||
f"Successfully paired with {device_name}",
|
||||
"--icon=bluetooth",
|
||||
"--urgency=normal",
|
||||
"--app-name=Bluetooth"
|
||||
], check=False)
|
||||
subprocess.run(
|
||||
[
|
||||
"notify-send",
|
||||
"Bluetooth Paired",
|
||||
f"Successfully paired with {device_name}",
|
||||
"--icon=bluetooth",
|
||||
"--urgency=normal",
|
||||
"--app-name=Bluetooth",
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to send pairing success notification: {e}")
|
||||
|
||||
@@ -145,28 +157,33 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
"""Show notification when pairing fails"""
|
||||
try:
|
||||
device_name = self.device.alias or self.device.name
|
||||
subprocess.run([
|
||||
"notify-send",
|
||||
"Bluetooth Pairing Failed",
|
||||
f"Failed to pair with {device_name}",
|
||||
"--icon=bluetooth-disabled",
|
||||
"--urgency=normal",
|
||||
"--app-name=Bluetooth"
|
||||
], check=False)
|
||||
subprocess.run(
|
||||
[
|
||||
"notify-send",
|
||||
"Bluetooth Pairing Failed",
|
||||
f"Failed to pair with {device_name}",
|
||||
"--icon=bluetooth-disabled",
|
||||
"--urgency=normal",
|
||||
"--app-name=Bluetooth",
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to send pairing failed notification: {e}")
|
||||
|
||||
def on_changed(self, *_):
|
||||
# Update connection icon
|
||||
self.connection_label.set_markup(
|
||||
icons.bluetooth_connected if self.device.connected else icons.bluetooth_disconnected
|
||||
icons.bluetooth_connected
|
||||
if self.device.connected
|
||||
else icons.bluetooth_disconnected
|
||||
)
|
||||
|
||||
# Check for pairing state changes and show notifications
|
||||
base_name = self.device.alias or self.device.name
|
||||
|
||||
# Track pairing success/failure
|
||||
if hasattr(self, '_was_pairing') and self._was_pairing:
|
||||
if hasattr(self, "_was_pairing") and self._was_pairing:
|
||||
if self.device.paired and not self.device.connecting:
|
||||
# Pairing succeeded
|
||||
self._show_pairing_success_notification()
|
||||
@@ -201,7 +218,6 @@ class BluetoothDeviceSlot(CenterBox):
|
||||
self.device_name_label.set_label(status_text)
|
||||
|
||||
|
||||
|
||||
class BluetoothTab:
|
||||
"""Bluetooth device management tab for settings"""
|
||||
|
||||
@@ -219,32 +235,27 @@ class BluetoothTab:
|
||||
"""Set the window size enforcer function from the main GUI"""
|
||||
self.window_size_enforcer = enforcer_func
|
||||
|
||||
|
||||
|
||||
def create_bluetooth_tab(self):
|
||||
"""Create the Bluetooth tab content"""
|
||||
main_vbox = Box(
|
||||
orientation="v",
|
||||
spacing=0,
|
||||
style="padding: 0; margin: 15px;"
|
||||
)
|
||||
main_vbox = Box(orientation="v", spacing=0, style="padding: 0; margin: 15px;")
|
||||
|
||||
# Set fixed size for the main container to match tab stack dimensions
|
||||
main_vbox.set_size_request(580, 580)
|
||||
|
||||
# Create widgets first
|
||||
self.bluetooth_subtitle = Label(
|
||||
markup="<span>Find and connect to Bluetooth devices</span>",
|
||||
h_align="start"
|
||||
markup="<span>Find and connect to Bluetooth devices</span>", h_align="start"
|
||||
)
|
||||
self.bluetooth_switch = Gtk.Switch()
|
||||
self.scan_icon = Label(name="bluetooth-tab-icon", markup=icons.radar, style="font-size: 18px;")
|
||||
self.scan_icon = Label(
|
||||
name="bluetooth-tab-icon", markup=icons.radar, style="font-size: 18px;"
|
||||
)
|
||||
self.scan_button = Button(
|
||||
name="bluetooth-scan",
|
||||
child=self.scan_icon,
|
||||
tooltip_text="Scan for devices",
|
||||
on_clicked=lambda *_: self.bluetooth_client.toggle_scan(),
|
||||
style="margin:10px;"
|
||||
style="margin:10px;",
|
||||
)
|
||||
|
||||
# Header section with title and controls
|
||||
@@ -258,10 +269,10 @@ class BluetoothTab:
|
||||
children=[
|
||||
Label(
|
||||
markup="<span size='large'><b>Bluetooth</b></span>",
|
||||
h_align="start"
|
||||
h_align="start",
|
||||
),
|
||||
self.bluetooth_subtitle,
|
||||
]
|
||||
],
|
||||
)
|
||||
],
|
||||
end_children=[
|
||||
@@ -271,14 +282,16 @@ class BluetoothTab:
|
||||
children=[
|
||||
self.bluetooth_switch,
|
||||
self.scan_button,
|
||||
]
|
||||
],
|
||||
)
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
# Set up switch
|
||||
self.bluetooth_switch.set_valign(Gtk.Align.CENTER)
|
||||
self.bluetooth_switch.connect("notify::active", self._on_bluetooth_switch_toggled)
|
||||
self.bluetooth_switch.connect(
|
||||
"notify::active", self._on_bluetooth_switch_toggled
|
||||
)
|
||||
|
||||
main_vbox.add(header_box)
|
||||
|
||||
@@ -292,11 +305,19 @@ class BluetoothTab:
|
||||
spacing=8,
|
||||
orientation="v",
|
||||
children=[
|
||||
Label(name="bluetooth-section", markup="<b>Paired Devices</b>", h_align="start"),
|
||||
Label(
|
||||
name="bluetooth-section",
|
||||
markup="<b>Paired Devices</b>",
|
||||
h_align="start",
|
||||
),
|
||||
self.paired_box,
|
||||
Label(name="bluetooth-section", markup="<b>Available Devices</b>", h_align="start"),
|
||||
Label(
|
||||
name="bluetooth-section",
|
||||
markup="<b>Available Devices</b>",
|
||||
h_align="start",
|
||||
),
|
||||
self.available_box,
|
||||
]
|
||||
],
|
||||
)
|
||||
# Set size constraints for content box
|
||||
content_box.set_size_request(560, -1)
|
||||
@@ -335,9 +356,7 @@ class BluetoothTab:
|
||||
self.scan_button.set_sensitive(True)
|
||||
else:
|
||||
self.bluetooth_switch.set_active(False)
|
||||
self.bluetooth_subtitle.set_markup(
|
||||
"<span>Bluetooth is turned off</span>"
|
||||
)
|
||||
self.bluetooth_subtitle.set_markup("<span>Bluetooth is turned off</span>")
|
||||
self.scan_button.set_sensitive(False)
|
||||
|
||||
def _on_bluetooth_changed(self, *args):
|
||||
@@ -362,9 +381,8 @@ class BluetoothTab:
|
||||
"""Populate existing devices when tab is created"""
|
||||
if not self.bluetooth_client or not self.bluetooth_client.enabled:
|
||||
return
|
||||
|
||||
|
||||
devices = self.bluetooth_client.devices
|
||||
|
||||
|
||||
for device in devices:
|
||||
self.on_device_added(self.bluetooth_client, device.address)
|
||||
@@ -430,7 +448,3 @@ class BluetoothTab:
|
||||
"""Clear and repopulate all devices"""
|
||||
self._clear_all_devices()
|
||||
self._populate_existing_devices()
|
||||
|
||||
|
||||
|
||||
|
||||
+18
-21
@@ -7,23 +7,19 @@ import time
|
||||
import gi
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.widgets.image import Image as FabricImage
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.stack import Stack
|
||||
from fabric.widgets.window import Window
|
||||
from gi.repository import Gdk, GdkPixbuf, GLib, Gtk
|
||||
from PIL import Image
|
||||
|
||||
from config.data import APP_NAME, APP_NAME_CAP
|
||||
from config.settings.utils import backup_and_replace, start_config
|
||||
from config.settings.wifi_tab import WiFiTab
|
||||
from config.settings.about_tab import AboutTab
|
||||
from config.settings.appearance_tab import AppearanceTab
|
||||
from config.settings.bluetooth_tab import BluetoothTab
|
||||
from config.settings.key_bindings_tab import KeyBindingsTab
|
||||
from config.settings.appearance_tab import AppearanceTab
|
||||
from config.settings.system_tab import SystemTab
|
||||
from config.settings.about_tab import AboutTab
|
||||
from config.settings.utils import backup_and_replace, start_config
|
||||
from config.settings.wifi_tab import WiFiTab
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
@@ -47,8 +43,9 @@ class HyprConfGUI(Window):
|
||||
geometry.max_width = 640
|
||||
geometry.min_height = 640
|
||||
geometry.max_height = 640
|
||||
self.set_geometry_hints(None, geometry,
|
||||
Gdk.WindowHints.MIN_SIZE | Gdk.WindowHints.MAX_SIZE)
|
||||
self.set_geometry_hints(
|
||||
None, geometry, Gdk.WindowHints.MIN_SIZE | Gdk.WindowHints.MAX_SIZE
|
||||
)
|
||||
|
||||
self.themes = ["Pills", "Dense", "Edge"]
|
||||
self.selected_face_icon = None
|
||||
@@ -60,7 +57,9 @@ class HyprConfGUI(Window):
|
||||
self.bluetooth_tab = BluetoothTab()
|
||||
self.key_bindings_tab = KeyBindingsTab()
|
||||
self.appearance_tab = AppearanceTab(self.themes, self)
|
||||
self.system_tab = SystemTab(show_lock_checkbox, show_idle_checkbox, self.enforce_window_size)
|
||||
self.system_tab = SystemTab(
|
||||
show_lock_checkbox, show_idle_checkbox, self.enforce_window_size
|
||||
)
|
||||
self.about_tab = AboutTab()
|
||||
|
||||
# Pass window size enforcement method to tabs
|
||||
@@ -72,7 +71,9 @@ class HyprConfGUI(Window):
|
||||
root_box.set_size_request(620, 620)
|
||||
self.add(root_box)
|
||||
|
||||
main_content_box = Box(orientation="h", spacing=6, v_expand=False, h_expand=False)
|
||||
main_content_box = Box(
|
||||
orientation="h", spacing=6, v_expand=False, h_expand=False
|
||||
)
|
||||
# Set fixed size for main content to prevent expansion
|
||||
main_content_box.set_size_request(620, 580)
|
||||
root_box.add(main_content_box)
|
||||
@@ -93,12 +94,8 @@ class HyprConfGUI(Window):
|
||||
self.system_tab_content = self.system_tab.create_system_tab()
|
||||
self.about_tab_content = self.about_tab.create_about_tab()
|
||||
|
||||
self.tab_stack.add_titled(
|
||||
self.wifi_tab_content, "Wifi", "Wifi"
|
||||
)
|
||||
self.tab_stack.add_titled(
|
||||
self.bluetooth_tab_content, "Bluetooth", "Bluetooth"
|
||||
)
|
||||
self.tab_stack.add_titled(self.wifi_tab_content, "Wifi", "Wifi")
|
||||
self.tab_stack.add_titled(self.bluetooth_tab_content, "Bluetooth", "Bluetooth")
|
||||
self.tab_stack.add_titled(
|
||||
self.key_bindings_tab_content, "key_bindings", "Key Bindings"
|
||||
)
|
||||
@@ -145,15 +142,15 @@ class HyprConfGUI(Window):
|
||||
"""Public method to enforce window size - can be called by tabs"""
|
||||
self._force_window_size(self)
|
||||
|
||||
|
||||
|
||||
# Event handlers moved to respective tab classes
|
||||
|
||||
def on_accept(self, _widget):
|
||||
current_bind_vars_snapshot = {}
|
||||
|
||||
# Get values from key bindings tab
|
||||
current_bind_vars_snapshot.update(self.key_bindings_tab.get_key_binding_values())
|
||||
current_bind_vars_snapshot.update(
|
||||
self.key_bindings_tab.get_key_binding_values()
|
||||
)
|
||||
|
||||
# Get values from appearance tab
|
||||
current_bind_vars_snapshot.update(self.appearance_tab.get_appearance_values())
|
||||
|
||||
@@ -8,7 +8,6 @@ from config.data import APP_NAME_CAP
|
||||
from config.settings.utils import bind_vars
|
||||
|
||||
|
||||
|
||||
class KeyBindingsTab:
|
||||
"""Key bindings management tab for settings"""
|
||||
|
||||
@@ -31,11 +30,7 @@ class KeyBindingsTab:
|
||||
main_vbox = Box(orientation="v", spacing=10, style="margin: 15px;")
|
||||
scrolled_window.add(main_vbox)
|
||||
|
||||
keybind_grid = Grid(
|
||||
column_spacing=10,
|
||||
row_spacing=8,
|
||||
style="margin: 5px;"
|
||||
)
|
||||
keybind_grid = Grid(column_spacing=10, row_spacing=8, style="margin: 5px;")
|
||||
|
||||
action_label = Label(
|
||||
markup="<b>Action</b>", h_align="start", style="margin-bottom: 5px;"
|
||||
|
||||
@@ -15,11 +15,16 @@ gi.require_version("Gtk", "3.0")
|
||||
class SystemTab:
|
||||
"""System settings and metrics management tab for settings"""
|
||||
|
||||
def __init__(self, show_lock_checkbox=False, show_idle_checkbox=False, window_size_enforcer=None):
|
||||
def __init__(
|
||||
self,
|
||||
show_lock_checkbox=False,
|
||||
show_idle_checkbox=False,
|
||||
window_size_enforcer=None,
|
||||
):
|
||||
self.show_lock_checkbox = show_lock_checkbox
|
||||
self.show_idle_checkbox = show_idle_checkbox
|
||||
self.window_size_enforcer = window_size_enforcer
|
||||
|
||||
|
||||
# Widget references
|
||||
self.terminal_entry = None
|
||||
self.lock_switch = None
|
||||
@@ -60,9 +65,7 @@ class SystemTab:
|
||||
def _create_terminal_hyprland_section(self, vbox):
|
||||
"""Create terminal and Hyprland integration section"""
|
||||
system_grid = Grid(
|
||||
column_spacing=20,
|
||||
row_spacing=10,
|
||||
style="margin-bottom: 15px;"
|
||||
column_spacing=20, row_spacing=10, style="margin-bottom: 15px;"
|
||||
)
|
||||
vbox.add(system_grid)
|
||||
|
||||
@@ -94,9 +97,7 @@ class SystemTab:
|
||||
)
|
||||
system_grid.attach(lock_label, 2, row, 1, 1)
|
||||
lock_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.lock_switch = Gtk.Switch(
|
||||
tooltip_text="Replace Hyprlock configuration with Modus's custom config"
|
||||
@@ -111,9 +112,7 @@ class SystemTab:
|
||||
)
|
||||
system_grid.attach(idle_label, 2, row, 1, 1)
|
||||
idle_switch_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
orientation="horizontal", h_align="start", v_align="center"
|
||||
)
|
||||
self.idle_switch = Gtk.Switch(
|
||||
tooltip_text="Replace Hypridle configuration with Modus's custom config"
|
||||
@@ -138,7 +137,7 @@ class SystemTab:
|
||||
notif_grid = Grid(
|
||||
column_spacing=20,
|
||||
row_spacing=10,
|
||||
style="margin-left: 10px; margin-top: 5px; margin-bottom: 15px;"
|
||||
style="margin-left: 10px; margin-top: 5px; margin-bottom: 15px;",
|
||||
)
|
||||
vbox.add(notif_grid)
|
||||
|
||||
@@ -191,7 +190,7 @@ class SystemTab:
|
||||
metrics_grid = Grid(
|
||||
column_spacing=15,
|
||||
row_spacing=8,
|
||||
style="margin-left: 10px; margin-top: 5px;"
|
||||
style="margin-left: 10px; margin-top: 5px;",
|
||||
)
|
||||
vbox.add(metrics_grid)
|
||||
|
||||
@@ -251,17 +250,15 @@ class SystemTab:
|
||||
self.disk_entries.set_size_request(530, -1)
|
||||
disk_entries_scrolled.add(self.disk_entries)
|
||||
|
||||
self._create_disk_edit_entry_func = lambda path: self._add_disk_entry_widget(path)
|
||||
self._create_disk_edit_entry_func = lambda path: self._add_disk_entry_widget(
|
||||
path
|
||||
)
|
||||
|
||||
for p in bind_vars.get("metrics_disks", ["/"]):
|
||||
self._create_disk_edit_entry_func(p)
|
||||
vbox.add(disk_entries_scrolled)
|
||||
|
||||
add_container = Box(
|
||||
orientation="horizontal",
|
||||
h_align="start",
|
||||
v_align="center"
|
||||
)
|
||||
add_container = Box(orientation="horizontal", h_align="start", v_align="center")
|
||||
add_btn = Button(
|
||||
label="Add new disk",
|
||||
on_clicked=lambda _: self._create_disk_edit_entry_func("/"),
|
||||
@@ -279,7 +276,9 @@ class SystemTab:
|
||||
x_btn = Button(label="X")
|
||||
x_btn.connect(
|
||||
"clicked",
|
||||
lambda _, current_bar_to_remove=bar: self._remove_disk_entry(current_bar_to_remove),
|
||||
lambda _, current_bar_to_remove=bar: self._remove_disk_entry(
|
||||
current_bar_to_remove
|
||||
),
|
||||
)
|
||||
bar.add(x_btn)
|
||||
self.disk_entries.add(bar)
|
||||
@@ -341,7 +340,7 @@ class SystemTab:
|
||||
"""Get the state of Hyprland integration switches"""
|
||||
return {
|
||||
"replace_lock": self.lock_switch and self.lock_switch.get_active(),
|
||||
"replace_idle": self.idle_switch and self.idle_switch.get_active()
|
||||
"replace_idle": self.idle_switch and self.idle_switch.get_active(),
|
||||
}
|
||||
|
||||
def reset_to_defaults(self, defaults):
|
||||
@@ -356,9 +355,7 @@ class SystemTab:
|
||||
enabled_switches = [s for s in switch_dict.values() if s.get_active()]
|
||||
can_disable = len(enabled_switches) > 1
|
||||
for s in switch_dict.values():
|
||||
s.set_sensitive(
|
||||
True if can_disable or not s.get_active() else False
|
||||
)
|
||||
s.set_sensitive(True if can_disable or not s.get_active() else False)
|
||||
|
||||
enforce_minimum_metrics(self.metrics_switches)
|
||||
|
||||
|
||||
+113
-80
@@ -1,3 +1,5 @@
|
||||
import subprocess
|
||||
|
||||
import gi
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
@@ -8,7 +10,6 @@ from gi.repository import GLib, Gtk
|
||||
|
||||
from services.network import NetworkClient
|
||||
from utils import icons
|
||||
import subprocess
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
@@ -64,11 +65,7 @@ class WiFiTab:
|
||||
|
||||
def create_wifi_tab(self):
|
||||
"""Create the WiFi tab content"""
|
||||
main_vbox = Box(
|
||||
orientation="v",
|
||||
spacing=0,
|
||||
style="padding: 0; margin: 15px;"
|
||||
)
|
||||
main_vbox = Box(orientation="v", spacing=0, style="padding: 0; margin: 15px;")
|
||||
|
||||
# Set fixed size for the main container to match tab stack dimensions
|
||||
main_vbox.set_size_request(580, 580)
|
||||
@@ -84,15 +81,13 @@ class WiFiTab:
|
||||
|
||||
# WiFi title
|
||||
wifi_title = Label(
|
||||
markup="<span size='large'><b>Wi-Fi</b></span>",
|
||||
h_align="start"
|
||||
markup="<span size='large'><b>Wi-Fi</b></span>", h_align="start"
|
||||
)
|
||||
wifi_section.add(wifi_title)
|
||||
|
||||
# WiFi subtitle
|
||||
self.wifi_subtitle = Label(
|
||||
markup="<span>Find and connect to Wi-Fi networks</span>",
|
||||
h_align="start"
|
||||
markup="<span>Find and connect to Wi-Fi networks</span>", h_align="start"
|
||||
)
|
||||
wifi_section.add(self.wifi_subtitle)
|
||||
|
||||
@@ -114,7 +109,9 @@ class WiFiTab:
|
||||
|
||||
# Refresh button
|
||||
self.refresh_button = Button()
|
||||
self.refresh_icon = Label(name="wifi-tab-icon", markup=icons.reload, style="font-size: 18px;")
|
||||
self.refresh_icon = Label(
|
||||
name="wifi-tab-icon", markup=icons.reload, style="font-size: 18px;"
|
||||
)
|
||||
self.refresh_button.add(self.refresh_icon)
|
||||
self.refresh_button.set_size_request(40, 40)
|
||||
self.refresh_button.set_tooltip_text("Refresh networks")
|
||||
@@ -164,8 +161,10 @@ class WiFiTab:
|
||||
self._update_wifi_status()
|
||||
|
||||
# Show available networks if WiFi is enabled
|
||||
if (self.network_client.wifi_device and
|
||||
self.network_client.wifi_device.wireless_enabled):
|
||||
if (
|
||||
self.network_client.wifi_device
|
||||
and self.network_client.wifi_device.wireless_enabled
|
||||
):
|
||||
self._populate_networks()
|
||||
|
||||
self.networks_container.show_all()
|
||||
@@ -177,9 +176,7 @@ class WiFiTab:
|
||||
def _update_wifi_status(self):
|
||||
"""Update WiFi status display"""
|
||||
if not self.network_client.wifi_device:
|
||||
self.wifi_subtitle.set_markup(
|
||||
"<span>No WiFi device available</span>"
|
||||
)
|
||||
self.wifi_subtitle.set_markup("<span>No WiFi device available</span>")
|
||||
self.wifi_switch.set_sensitive(False)
|
||||
return
|
||||
|
||||
@@ -191,9 +188,7 @@ class WiFiTab:
|
||||
self.refresh_button.set_sensitive(True)
|
||||
else:
|
||||
self.wifi_switch.set_active(False)
|
||||
self.wifi_subtitle.set_markup(
|
||||
"<span>Wi-Fi is turned off</span>"
|
||||
)
|
||||
self.wifi_subtitle.set_markup("<span>Wi-Fi is turned off</span>")
|
||||
self.refresh_button.set_sensitive(False)
|
||||
|
||||
def _populate_networks(self):
|
||||
@@ -205,15 +200,13 @@ class WiFiTab:
|
||||
access_points = self.network_client.wifi_device.access_points
|
||||
if not access_points:
|
||||
self._show_status_message(
|
||||
"No networks found\nClick 'Scan for Networks' to refresh",
|
||||
"info"
|
||||
"No networks found\nClick 'Scan for Networks' to refresh", "info"
|
||||
)
|
||||
return
|
||||
|
||||
# Sort access points by signal strength (connected first, then by strength)
|
||||
sorted_aps = sorted(
|
||||
access_points,
|
||||
key=lambda ap: (not ap.is_active, -ap.strength)
|
||||
access_points, key=lambda ap: (not ap.is_active, -ap.strength)
|
||||
)
|
||||
|
||||
for ap in sorted_aps:
|
||||
@@ -221,11 +214,7 @@ class WiFiTab:
|
||||
|
||||
def _show_status_message(self, message, message_type="info"):
|
||||
"""Show a status message in the networks container"""
|
||||
status_box = Box(
|
||||
orientation="v",
|
||||
spacing=16,
|
||||
style="padding: 60px;"
|
||||
)
|
||||
status_box = Box(orientation="v", spacing=16, style="padding: 60px;")
|
||||
status_box.set_halign(Gtk.Align.CENTER)
|
||||
status_box.set_valign(Gtk.Align.CENTER)
|
||||
|
||||
@@ -235,24 +224,26 @@ class WiFiTab:
|
||||
message_label = Label(
|
||||
markup=f"<span size='medium'>{message}</span>",
|
||||
h_align="center",
|
||||
style="color: var(--error);"
|
||||
style="color: var(--error);",
|
||||
)
|
||||
elif message_type == "scanning":
|
||||
icon_markup = icons.loader
|
||||
message_label = Label(
|
||||
markup=f"<span size='medium'>{message}</span>",
|
||||
h_align="center",
|
||||
style="color: var(--blue);"
|
||||
style="color: var(--blue);",
|
||||
)
|
||||
else: # info
|
||||
icon_markup = icons.wifi_off
|
||||
message_label = Label(
|
||||
markup=f"<span size='medium'>{message}</span>",
|
||||
h_align="center",
|
||||
style="color: var(--outline);"
|
||||
style="color: var(--outline);",
|
||||
)
|
||||
|
||||
icon_label = Label(name="wifi-tab-icon", markup=icon_markup, style="font-size: 18px;")
|
||||
icon_label = Label(
|
||||
name="wifi-tab-icon", markup=icon_markup, style="font-size: 18px;"
|
||||
)
|
||||
icon_label.set_halign(Gtk.Align.CENTER)
|
||||
status_box.add(icon_label)
|
||||
message_label.set_line_wrap(True)
|
||||
@@ -271,7 +262,10 @@ class WiFiTab:
|
||||
network_button = Button(
|
||||
style="background-color: transparent; border: none; padding: 0; margin: 0;"
|
||||
)
|
||||
network_button.connect("clicked", lambda btn, access_point=ap: self._on_network_clicked(access_point))
|
||||
network_button.connect(
|
||||
"clicked",
|
||||
lambda btn, access_point=ap: self._on_network_clicked(access_point),
|
||||
)
|
||||
|
||||
# Network info row
|
||||
info_box = Box(orientation="h", spacing=12)
|
||||
@@ -291,7 +285,9 @@ class WiFiTab:
|
||||
else:
|
||||
icon_markup = icons.wifi_0
|
||||
|
||||
signal_label = Label(name="wifi-tab-icon", markup=icon_markup, style="font-size: 24px;")
|
||||
signal_label = Label(
|
||||
name="wifi-tab-icon", markup=icon_markup, style="font-size: 24px;"
|
||||
)
|
||||
signal_label.set_size_request(30, -1)
|
||||
info_box.add(signal_label)
|
||||
|
||||
@@ -299,10 +295,18 @@ class WiFiTab:
|
||||
network_name = ap.ssid
|
||||
if ap.is_active:
|
||||
network_markup = f"<b>{network_name}</b>\nConnected"
|
||||
network_label = Label(markup=network_markup, h_align="start", style="color: var(--foreground);")
|
||||
network_label = Label(
|
||||
markup=network_markup,
|
||||
h_align="start",
|
||||
style="color: var(--foreground);",
|
||||
)
|
||||
else:
|
||||
network_markup = f"{network_name}"
|
||||
network_label = Label(markup=network_markup, h_align="start", style="color: var(--foreground);")
|
||||
network_label = Label(
|
||||
markup=network_markup,
|
||||
h_align="start",
|
||||
style="color: var(--foreground);",
|
||||
)
|
||||
network_label.set_line_wrap(True)
|
||||
info_box.add(network_label)
|
||||
|
||||
@@ -313,7 +317,9 @@ class WiFiTab:
|
||||
|
||||
# Lock icon for secured networks
|
||||
if ap.requires_password:
|
||||
lock_label = Label(name="wifi-tab-icon", markup=icons.lock, style="font-size: 22px;")
|
||||
lock_label = Label(
|
||||
name="wifi-tab-icon", markup=icons.lock, style="font-size: 22px;"
|
||||
)
|
||||
info_box.add(lock_label)
|
||||
|
||||
network_button.add(info_box)
|
||||
@@ -321,11 +327,11 @@ class WiFiTab:
|
||||
|
||||
# Store reference
|
||||
self.access_point_widgets[ap.ssid] = {
|
||||
'container': network_container,
|
||||
'button': network_button,
|
||||
'info_box': info_box,
|
||||
'network_label': network_label,
|
||||
'ap': ap
|
||||
"container": network_container,
|
||||
"button": network_button,
|
||||
"info_box": info_box,
|
||||
"network_label": network_label,
|
||||
"ap": ap,
|
||||
}
|
||||
|
||||
self.networks_container.add(network_container)
|
||||
@@ -353,7 +359,10 @@ class WiFiTab:
|
||||
|
||||
def _on_refresh_clicked(self, button):
|
||||
"""Handle refresh button click"""
|
||||
if self.network_client.wifi_device and self.network_client.wifi_device.wireless_enabled:
|
||||
if (
|
||||
self.network_client.wifi_device
|
||||
and self.network_client.wifi_device.wireless_enabled
|
||||
):
|
||||
self.refresh_icon.set_markup(icons.loader)
|
||||
button.set_sensitive(False)
|
||||
|
||||
@@ -366,11 +375,14 @@ class WiFiTab:
|
||||
self.network_client.wifi_device.scan()
|
||||
|
||||
# Re-enable button after scan
|
||||
GLib.timeout_add(3000, lambda: [
|
||||
self.refresh_icon.set_markup(icons.reload),
|
||||
button.set_sensitive(True),
|
||||
self._refresh_networks()
|
||||
])
|
||||
GLib.timeout_add(
|
||||
3000,
|
||||
lambda: [
|
||||
self.refresh_icon.set_markup(icons.reload),
|
||||
button.set_sensitive(True),
|
||||
self._refresh_networks(),
|
||||
],
|
||||
)
|
||||
|
||||
def _connect_network(self, ap):
|
||||
"""Connect to a network"""
|
||||
@@ -380,7 +392,7 @@ class WiFiTab:
|
||||
if ap.ssid in self.password_entries:
|
||||
# Get password from stored entry widget
|
||||
password_widget = self.password_entries[ap.ssid]
|
||||
if hasattr(password_widget, 'get_text'):
|
||||
if hasattr(password_widget, "get_text"):
|
||||
password = password_widget.get_text()
|
||||
else:
|
||||
password = str(password_widget) # If it's stored as string
|
||||
@@ -397,7 +409,7 @@ class WiFiTab:
|
||||
|
||||
# Update network widget to show connecting state
|
||||
if ap.ssid in self.access_point_widgets:
|
||||
network_label = self.access_point_widgets[ap.ssid]['network_label']
|
||||
network_label = self.access_point_widgets[ap.ssid]["network_label"]
|
||||
network_label.set_markup(f"{ap.ssid}\nConnecting...")
|
||||
network_label.set_style("color: var(--blue);")
|
||||
|
||||
@@ -416,24 +428,25 @@ class WiFiTab:
|
||||
self._reset_connection_state(ap.ssid)
|
||||
|
||||
def _show_connection_error(self, ssid, error_message):
|
||||
|
||||
try:
|
||||
|
||||
subprocess.run([
|
||||
"notify-send",
|
||||
"WiFi Connection Failed",
|
||||
f"Failed to connect to {ssid}\n{error_message}",
|
||||
"--icon=network-wireless-offline",
|
||||
"--urgency=normal",
|
||||
"--app-name=WiFi"
|
||||
], check=False)
|
||||
subprocess.run(
|
||||
[
|
||||
"notify-send",
|
||||
"WiFi Connection Failed",
|
||||
f"Failed to connect to {ssid}\n{error_message}",
|
||||
"--icon=network-wireless-offline",
|
||||
"--urgency=normal",
|
||||
"--app-name=WiFi",
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to send connection error notification: {e}")
|
||||
|
||||
def _show_password_entry(self, ap):
|
||||
"""Show password entry for a network"""
|
||||
if ap.ssid in self.access_point_widgets:
|
||||
container = self.access_point_widgets[ap.ssid]['container']
|
||||
container = self.access_point_widgets[ap.ssid]["container"]
|
||||
|
||||
# Check if password entry is already shown
|
||||
if len(container.get_children()) > 1:
|
||||
@@ -443,42 +456,60 @@ class WiFiTab:
|
||||
password_box = Box(
|
||||
orientation="h",
|
||||
spacing=8,
|
||||
style="padding: 8px; margin: 4px 16px 8px 16px; border-radius: 8px;"
|
||||
style="padding: 8px; margin: 4px 16px 8px 16px; border-radius: 8px;",
|
||||
)
|
||||
|
||||
password_entry = Entry(
|
||||
placeholder_text="Enter network password",
|
||||
style="background-color: var(--surface-bright); border: 1px solid var(--outline); border-radius: 8px;"
|
||||
style="background-color: var(--surface-bright); border: 1px solid var(--outline); border-radius: 8px;",
|
||||
)
|
||||
password_entry.set_visibility(False) # Hide password characters
|
||||
password_entry.set_hexpand(True)
|
||||
password_entry.connect("activate", lambda entry, access_point=ap: self._on_password_entered(access_point, entry))
|
||||
password_entry.connect(
|
||||
"activate",
|
||||
lambda entry, access_point=ap: self._on_password_entered(
|
||||
access_point, entry
|
||||
),
|
||||
)
|
||||
password_box.add(password_entry)
|
||||
|
||||
# Show/Hide password toggle
|
||||
show_password_btn = Button(
|
||||
style="border-radius: 8px;"
|
||||
show_password_btn = Button(style="border-radius: 8px;")
|
||||
self.show_password_icon = Label(
|
||||
name="wifi-tab-icon", markup=icons.spy, style="font-size: 18px;"
|
||||
)
|
||||
self.show_password_icon = Label(name="wifi-tab-icon", markup=icons.spy,style="font-size: 18px;")
|
||||
show_password_btn.add(self.show_password_icon)
|
||||
show_password_btn.set_tooltip_text("Show/Hide password")
|
||||
show_password_btn.connect("clicked", lambda btn, entry=password_entry: self._toggle_password_visibility(entry, btn))
|
||||
show_password_btn.connect(
|
||||
"clicked",
|
||||
lambda btn, entry=password_entry: self._toggle_password_visibility(
|
||||
entry, btn
|
||||
),
|
||||
)
|
||||
password_box.add(show_password_btn)
|
||||
|
||||
connect_btn = Button(
|
||||
style="border-radius: 8px;"
|
||||
connect_btn = Button(style="border-radius: 8px;")
|
||||
connect_icon = Label(
|
||||
name="wifi-tab-icon", markup=icons.connect, style="font-size: 18px;"
|
||||
)
|
||||
connect_icon = Label(name="wifi-tab-icon", markup=icons.connect,style="font-size: 18px;")
|
||||
connect_btn.add(connect_icon)
|
||||
connect_btn.connect("clicked", lambda btn, access_point=ap, entry=password_entry: self._on_password_entered(access_point, entry))
|
||||
connect_btn.connect(
|
||||
"clicked",
|
||||
lambda btn,
|
||||
access_point=ap,
|
||||
entry=password_entry: self._on_password_entered(access_point, entry),
|
||||
)
|
||||
password_box.add(connect_btn)
|
||||
|
||||
cancel_btn = Button(
|
||||
style="border-radius: 8px;"
|
||||
cancel_btn = Button(style="border-radius: 8px;")
|
||||
cancel_icon = Label(
|
||||
name="wifi-tab-icon", markup=icons.cancel, style="font-size: 18px;"
|
||||
)
|
||||
cancel_icon = Label(name="wifi-tab-icon", markup=icons.cancel,style="font-size: 18px;")
|
||||
cancel_btn.add(cancel_icon)
|
||||
cancel_btn.connect("clicked", lambda btn, access_point=ap: self._hide_password_entry(access_point))
|
||||
cancel_btn.connect(
|
||||
"clicked",
|
||||
lambda btn, access_point=ap: self._hide_password_entry(access_point),
|
||||
)
|
||||
password_box.add(cancel_btn)
|
||||
|
||||
container.add(password_box)
|
||||
@@ -493,7 +524,9 @@ class WiFiTab:
|
||||
current_visibility = entry.get_visibility()
|
||||
entry.set_visibility(not current_visibility)
|
||||
# Use spy icon for hidden, key icon for visible
|
||||
self.show_password_icon.set_markup(icons.key if not current_visibility else icons.spy)
|
||||
self.show_password_icon.set_markup(
|
||||
icons.key if not current_visibility else icons.spy
|
||||
)
|
||||
|
||||
def _on_password_entered(self, ap, entry):
|
||||
"""Handle password entry"""
|
||||
@@ -505,7 +538,7 @@ class WiFiTab:
|
||||
def _hide_password_entry(self, ap):
|
||||
"""Hide password entry for a network"""
|
||||
if ap.ssid in self.access_point_widgets:
|
||||
container = self.access_point_widgets[ap.ssid]['container']
|
||||
container = self.access_point_widgets[ap.ssid]["container"]
|
||||
children = container.get_children()
|
||||
if len(children) > 1: # Remove password entry box
|
||||
container.remove(children[-1])
|
||||
@@ -520,8 +553,8 @@ class WiFiTab:
|
||||
"""Reset connection state for a network"""
|
||||
if ssid in self.access_point_widgets:
|
||||
# Reset the network label to normal state
|
||||
ap = self.access_point_widgets[ssid]['ap']
|
||||
network_label = self.access_point_widgets[ssid]['network_label']
|
||||
ap = self.access_point_widgets[ssid]["ap"]
|
||||
network_label = self.access_point_widgets[ssid]["network_label"]
|
||||
if ap.is_active:
|
||||
network_markup = f"<b>{ap.ssid}</b>\nConnected"
|
||||
network_label.set_style("color: var(--foreground);")
|
||||
|
||||
Reference in new issue
Block a user