Huge Updates

This commit is contained in:
sankalp committed 2025-02-25 05:21:53 +05:45
1 parent 28f47aed3c
commit 1885a2dc30
30 files changed
+1421 -1260

No files matched your search

+26 -22
View File
@@ -1,6 +1,11 @@
from fabric.hyprland.widgets import Language
from fabric.system_tray.widgets import SystemTray
from fabric.utils import FormattedString, bulk_replace
from fabric.utils import (
FormattedString,
bulk_replace,
exec_shell_command_async,
get_relative_path,
)
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
@@ -10,16 +15,14 @@ from fabric.widgets.wayland import WaylandWindow as Window
from gi.repository import GLib
from modules.bar.components import (
BatteryLabel,
BluetoothIndicator,
MicrophoneIndicator,
NetworkIndicator,
SystemInfo,
TaskBar,
VolumeIndicator,
workspace,
Indicators,
UpdatesWidget,
)
from snippets import MaterialIcon
from services import sc
import snippets.iconss as icons
class Bar(Window):
@@ -39,7 +42,7 @@ class Bar(Window):
self.recording_indicator = Button(
name="recording-indicator",
child=MaterialIcon("stop_circle", 16),
child=Label(name="recorder", markup=icons.record),
visible=False,
on_clicked=lambda *_: sc.screencast_stop(),
)
@@ -50,13 +53,9 @@ class Bar(Window):
self.date_time = DateTime(formatters=["%-I:%M 󰧞 %a %d %b"], name="datetime")
self.battery = BatteryLabel(name="battery")
self.volume = VolumeIndicator()
self.bluetooth = BluetoothIndicator()
self.network = NetworkIndicator()
self.microphone = MicrophoneIndicator()
self.battery = BatteryLabel()
self.taskbar = TaskBar()
self.info = SystemInfo()
self.stats = SystemInfo()
self.tray = SystemTray(name="tray", icon_size=16, spacing=4)
self.launcher = Button(
name="logo",
@@ -65,6 +64,15 @@ class Bar(Window):
"fabric-cli exec modus 'launcher.open(\"launcher\")'"
),
)
self.updates = UpdatesWidget()
self.indicators = Indicators()
self.button_config = Button(
name="button-bar",
on_clicked=lambda *_: exec_shell_command_async(
f"python {get_relative_path('../../config/config.py')}"
),
child=Label(name="button-bar-label", markup=icons.config),
)
self.applets = Box(
name="applets",
@@ -72,10 +80,7 @@ class Bar(Window):
orientation="h",
children=[
self.language,
self.bluetooth,
self.network,
self.volume,
self.microphone,
self.indicators,
],
)
@@ -85,11 +90,7 @@ class Bar(Window):
name="start-container",
spacing=8,
orientation="h",
children=[
self.launcher,
self.workspaces,
self.info,
],
children=[self.launcher, self.workspaces, self.stats, self.updates],
),
center_children=Box(
name="center-container",
@@ -107,6 +108,7 @@ class Bar(Window):
self.battery,
self.applets,
self.date_time,
self.button_config,
],
),
)
@@ -127,6 +129,8 @@ class Bar(Window):
def toggle_hidden(self):
self.hidden = not self.hidden
if self.hidden:
self.bar.remove_style_class("visible")
self.bar.add_style_class("hidden")
else:
self.bar.remove_style_class("hidden")
self.bar.add_style_class("visible")
+2 -6
View File
@@ -1,10 +1,6 @@
from .battery import *
from .bluetooth import *
from .idle import *
from .stats import *
from .microphone import *
from .network import *
from .powerprofile import *
from .taskbar import *
from .volume import *
from .workspace import *
from .indicators import *
from .updates import *
+226 -59
View File
@@ -1,70 +1,237 @@
import psutil
from fabric import Fabricator
import subprocess
import re
from fabric.widgets.box import Box
from fabric.widgets.overlay import Overlay
from fabric.widgets.eventbox import EventBox
from fabric.widgets.button import Button
from fabric.widgets.label import Label
from fabric.widgets.circularprogressbar import CircularProgressBar
from snippets import MaterialIcon
from fabric.widgets.overlay import Overlay
from fabric.widgets.revealer import Revealer
from fabric.core.fabricator import Fabricator
from fabric.utils.helpers import exec_shell_command_async
from gi.repository import GLib
import snippets.iconss as icons
class BatteryLabel(Box):
ICONS_CHARGING = [
"battery_charging_20",
"battery_charging_20",
"battery_charging_20",
"battery_charging_30",
"battery_charging_30",
"battery_charging_50",
"battery_charging_60",
"battery_charging_80",
"battery_charging_80",
"battery_charging_90",
"battery_charging_full",
]
ICONS_NOT_CHARGING = [
"battery_alert",
"battery_1_bar",
"battery_1_bar",
"battery_2_bar",
"battery_2_bar",
"battery_3_bar",
"battery_4_bar",
"battery_4_bar",
"battery_5_bar",
"battery_6_bar",
"battery_full",
]
def __init__(self, **kwargs):
super().__init__(**kwargs)
Fabricator(interval=1000, poll_from=self.update_battery_status)
def update_battery_status(self, *_):
battery = psutil.sensors_battery()
if battery is None:
self.hide()
return
is_charging = battery.power_plugged if battery else False
icons = self.ICONS_CHARGING if is_charging else self.ICONS_NOT_CHARGING
battery_percent = round(battery.percent)
index = min(max(battery_percent // 10, 0), 10)
battery_icon = MaterialIcon(icons[index], size=16)
self.progress_bar = CircularProgressBar(
name="progress", line_style="round", line_width=2, size=24
super().__init__(
name="battery",
orientation="h",
spacing=0,
)
self.battery_overlay = Overlay(child=self.progress_bar, overlays=battery_icon)
self.battery_label = Label(label=f"{battery_percent}%", name="battery-label")
self.children = self.battery_label, self.battery_overlay
battery_percent = round(battery.percent)
self.progress_bar.value = battery_percent / 100
self.show() if battery_percent < 100 else self.hide()
self.set_tooltip_text(
f"Battery: {battery_percent}%\nStatus: {'Charging' if is_charging else 'Discharging'}"
self.bat_save = Button(
name="battery-save",
child=Label(name="battery-save-label", markup=icons.power_saving),
on_clicked=lambda *_: self.set_power_mode("powersave"),
)
self.bat_balanced = Button(
name="battery-balanced",
child=Label(name="battery-balanced-label", markup=icons.power_balanced),
on_clicked=lambda *_: self.set_power_mode("balanced"),
)
self.bat_perf = Button(
name="battery-performance",
child=Label(
name="battery-performance-label", markup=icons.power_performance
),
on_clicked=lambda *_: self.set_power_mode("performance"),
)
return True
# Attach mouse enter/leave events to buttons as well
for btn in [self.bat_save, self.bat_balanced, self.bat_perf]:
btn.connect("enter-notify-event", self.on_mouse_enter)
btn.connect("leave-notify-event", self.on_mouse_leave)
# Group the mode buttons into a container.
self.mode_switcher = Box(
name="power-mode-switcher",
orientation="h",
spacing=4,
children=[self.bat_save, self.bat_balanced, self.bat_perf],
)
self.bat_icon = Label(name="battery-icon", markup=icons.battery)
self.bat_circle = CircularProgressBar(
name="battery-circle",
value=0,
size=28,
line_width=2,
start_angle=180,
end_angle=360,
)
self.bat_overlay = Overlay(
name="battery-overlay",
visible=False,
child=self.bat_circle,
overlays=[self.bat_icon],
)
self.bat_level = Label(
name="battery-level",
label="100%",
)
self.bat_revealer = Revealer(
name="battery-revealer",
transition_duration=250,
transition_type="slide-left",
child=self.bat_level,
child_revealed=False,
)
# Create an inner container to hold the battery elements.
inner_container = Box(orientation="h", spacing=3)
inner_container.add(self.bat_overlay)
inner_container.add(self.bat_revealer)
inner_container.add(self.mode_switcher)
# Wrap the inner container in an EventBox.
self.event_box = EventBox(
events=["enter-notify-event", "leave-notify-event"],
name="battery-eventbox",
)
self.event_box.connect("enter-notify-event", self.on_mouse_enter)
self.event_box.connect("leave-notify-event", self.on_mouse_leave)
self.event_box.add(inner_container)
# Add the EventBox to the Battery widget.
self.add(self.event_box)
self.current_mode = None
# Initialize hide_timer for delayed hiding.
self.hide_timer = None
# Initialize counter to track hover status across all related widgets.
self.hover_counter = 0
# Initialize Fabricator for battery polling every 5 seconds.
self.batt_fabricator = Fabricator(
lambda *args, **kwargs: self.poll_battery(),
interval=1000,
stream=False,
default_value=0,
)
self.batt_fabricator.changed.connect(self.update_battery)
# Run update_battery immediately at startup.
GLib.idle_add(self.update_battery, None, self.poll_battery())
self.set_power_mode("balanced")
def on_mouse_enter(self, widget, event):
"""Reveal battery level on hover."""
self.hover_counter += 1
if self.hide_timer is not None:
GLib.source_remove(self.hide_timer)
self.hide_timer = None
self.bat_revealer.set_reveal_child(True)
return False
def on_mouse_leave(self, widget, event):
"""Schedule hiding the battery level after a 0.5s delay only if not hovering any element."""
if self.hover_counter > 0:
self.hover_counter -= 1
if self.hover_counter == 0:
if self.hide_timer is not None:
GLib.source_remove(self.hide_timer)
self.hide_timer = GLib.timeout_add(500, self.hide_revealer)
return False
def hide_revealer(self):
self.bat_revealer.set_reveal_child(False)
self.hide_timer = None
return False
def poll_battery(self):
"""
Polls the battery status by running the "acpi -b" command.
If no battery information is found, returns (0, None).
Otherwise, returns a tuple: (battery percentage as a float between 0 and 1, battery status string)
"""
try:
output = subprocess.check_output(["acpi", "-b"]).decode("utf-8").strip()
if "Battery" not in output:
return (0, None)
match_percent = re.search(r"(\d+)%", output)
match_status = re.search(r"Battery \d+: (\w+)", output)
if match_percent:
percent = int(match_percent.group(1))
status = match_status.group(1) if match_status else None
return (percent / 100.0, status)
except Exception:
pass
return (0, None)
def update_battery(self, sender, battery_data):
"""
Updates the battery widget...
"""
value, status = battery_data
if value == 0:
self.bat_overlay.set_visible(False)
self.bat_revealer.set_visible(False)
else:
self.bat_overlay.set_visible(True)
self.bat_revealer.set_visible(True)
self.bat_circle.set_value(value)
percentage = int(value * 100)
self.bat_level.set_label(f"{percentage}%")
# Actualizar el icono de la batería basado en el nivel y el estado de carga.
if percentage <= 15:
self.bat_icon.set_markup(icons.alert)
self.bat_icon.add_style_class("alert")
self.bat_circle.add_style_class("alert")
else:
self.bat_icon.remove_style_class("alert")
self.bat_circle.remove_style_class("alert")
if (
status == "Discharging"
): # Muestra discharging en cualquier nivel, incluso al 100%.
self.bat_icon.set_markup(icons.discharging)
elif percentage == 100:
self.bat_icon.set_markup(icons.battery)
elif status == "Charging":
self.bat_icon.set_markup(icons.charging)
else:
self.bat_icon.set_markup(icons.battery)
def set_power_mode(self, mode):
"""
Switches power mode by running the corresponding auto-cpufreq command.
mode: one of 'powersave', 'balanced', or 'performance'
"""
commands = {
"powersave": "sudo auto-cpufreq --force powersave",
"balanced": "sudo auto-cpufreq --force reset",
"performance": "sudo auto-cpufreq --force performance",
}
if mode in commands:
try:
exec_shell_command_async(commands[mode])
self.current_mode = mode
self.update_button_styles()
except Exception as err:
# Optionally, handle errors or display a notification.
print(f"Error setting power mode: {err}")
def update_button_styles(self):
"""
Optionally updates button styles to reflect the current mode.
Adjust the styling method based on your toolkit's capabilities.
"""
if self.current_mode == "powersave":
self.bat_save.add_style_class("active")
self.bat_balanced.remove_style_class("active")
self.bat_perf.remove_style_class("active")
elif self.current_mode == "balanced":
self.bat_save.remove_style_class("active")
self.bat_balanced.add_style_class("active")
self.bat_perf.remove_style_class("active")
elif self.current_mode == "performance":
self.bat_save.remove_style_class("active")
self.bat_balanced.remove_style_class("active")
self.bat_perf.add_style_class("active")
-34
View File
@@ -1,34 +0,0 @@
from fabric.bluetooth import BluetoothClient
from fabric.widgets.box import Box
from snippets import MaterialIcon
class BluetoothIndicator(Box):
ICON_ENABLED = "bluetooth"
ICON_DISABLED = "bluetooth_disabled"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bluetooth_client = BluetoothClient()
self.bluetooth_icon = None
self.bluetooth_client.connect("changed", self.update_bluetooth_status)
self.update_bluetooth_status()
def update_bluetooth_status(self, *_):
icon_label = (
self.ICON_ENABLED if self.bluetooth_client.enabled else self.ICON_DISABLED
)
new_icon = MaterialIcon(icon_label, size=16)
if self.bluetooth_icon:
self.remove(self.bluetooth_icon)
self.bluetooth_icon = new_icon
self.add(self.bluetooth_icon)
def on_destroy(self):
self.bluetooth_client.disconnect("changed", self.update_bluetooth_status)
+134
View File
@@ -0,0 +1,134 @@
import subprocess
from fabric import Fabricator
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from fabric.bluetooth import BluetoothClient
from services import NetworkClient, audio
import snippets.iconss as icons
class Indicators(Box):
def __init__(self, **kwargs):
super().__init__(orientation="h", spacing=2, **kwargs)
self.bluetooth_icon = Label(name="indicator-icon")
self.wifi_icon = Label(name="indicator-icon")
self.volume_icon_button = Label(name="indicator-icon")
self.microphone_icon = Label(name="indicator-icon")
self.idle_label = Label(name="indicator-icon")
self.night_label = Label(name="indicator-icon")
for widget in [
self.bluetooth_icon,
self.wifi_icon,
self.volume_icon_button,
self.microphone_icon,
self.idle_label,
self.night_label,
]:
self.add(widget)
self.audio_service = audio
self.bluetooth_client = BluetoothClient()
self.network_client = NetworkClient()
# Connect signals
self.bluetooth_client.connect("changed", self.update_bluetooth_status)
self.audio_service.connect("microphone_changed", self.update_mic_status)
self.audio_service.connect("changed", self.update_volume_status)
self.network_client.connect("device_ready", self.update_network_status)
if self.network_client.wifi_device:
self.network_client.wifi_device.connect(
"changed", self.update_network_status
)
if self.network_client.ethernet_device:
self.network_client.ethernet_device.connect(
"changed", self.update_network_status
)
Fabricator(interval=1000, poll_from=self.update_all_statuses)
self.update_bluetooth_status()
def update_all_statuses(self, *_args):
self.update_idle_night_status()
self.update_network_status()
def update_idle_night_status(self, *_):
"""Updates the idle and night mode status icons."""
self.idle_label.set_markup(
icons.coffee
if subprocess.run(
["pgrep", "-x", "wlinhibit"], capture_output=True
).returncode
== 0
else ""
)
self.night_label.set_markup(
icons.night
if subprocess.run(
["pgrep", "-x", "hyprsunset"], capture_output=True
).returncode
== 0
else ""
)
def update_volume_status(self, *_):
"""Updates the volume icon based on speaker status."""
stream = self.audio_service.speaker
if stream:
volume_level = stream.volume
self.volume_icon_button.set_markup(
icons.vol_off
if stream.muted
else (
icons.vol_mute
if volume_level == 0
else (icons.vol_medium if volume_level <= 50 else icons.vol_high)
)
)
def update_mic_status(self, *_):
"""Updates the microphone icon based on mute status."""
mic = self.audio_service.microphone
self.microphone_icon.set_markup(icons.mic_off if mic and mic.muted else "")
self.microphone_icon.set_visible(bool(mic))
def update_bluetooth_status(self, *_):
"""Updates the Bluetooth icon based on enabled status."""
self.bluetooth_icon.set_markup(
icons.bluetooth if self.bluetooth_client.enabled else icons.bluetooth_off
)
def update_network_status(self, *_):
primary_device = self.network_client.primary_device
if primary_device == "wifi" and self.network_client.wifi_device:
wifi_device = self.network_client.wifi_device
self.set_tooltip_text(self.network_client.wifi_device.ssid)
if wifi_device.enabled:
wifi_strength = wifi_device.strength
if wifi_strength == -1: # No active connection
icon_label = icons.wifi_off
else:
icon_label = {
80: icons.wifi,
60: icons.good_signal,
40: icons.moderate_signal,
20: icons.weak_signal,
0: icons.no_signal,
}.get(min(80, 20 * round(wifi_strength / 20)), icons.wifi_off)
else:
icon_label = icons.wifi_off
elif primary_device == "wired":
icon_label = icons.lan
else:
icon_label = icons.wifi_off
self.wifi_icon.set_markup(icon_label)
-33
View File
@@ -1,33 +0,0 @@
from fabric.audio.service import Audio
from fabric.widgets.box import Box
from snippets import MaterialIcon
class MicrophoneIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.audio_service = Audio()
self.microphone_icon = self.create_microphone_icon()
self.audio_service.connect("microphone_changed", self.update_microphone_status)
self.update_microphone_status()
self.children = (self.microphone_icon,)
def create_microphone_icon(self):
return MaterialIcon("mic_off", size=16)
def update_microphone_status(self, *args):
current_microphone = self.audio_service.microphone
if current_microphone:
is_muted = current_microphone.muted
icon_name = "mic_off" if is_muted else "mic"
self.microphone_icon.set_label(icon_name)
self.microphone_icon.set_visible(is_muted)
else:
self.microphone_icon.set_visible(False)
return True
-84
View File
@@ -1,84 +0,0 @@
import subprocess
import psutil
from fabric import Fabricator
from fabric.widgets.box import Box
from snippets import MaterialIcon
class NetworkIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.update_interval = 5000 # Increased interval to 5 seconds
self.last_status = None
self.wifi_strength_cache = None
self.fabricator = Fabricator(
interval=self.update_interval,
poll_from=self.update_network_status,
)
def update_network_status(self, *_):
current_status = self.get_network_status()
if current_status != self.last_status:
self.update_icon(current_status)
self.last_status = current_status
return True
def update_icon(self, current_status):
self.icon_label = MaterialIcon(current_status, size=16)
self.children = (self.icon_label,)
def get_network_status(self):
if self.is_ethernet_connected():
return "lan"
if self.is_wifi_connected():
return self.get_wifi_strength_icon()
return "signal_wifi_off"
def get_wifi_strength_icon(self):
wifi_strength = self.check_wifi_strength()
if wifi_strength is not None:
self.wifi_strength_cache = wifi_strength # Cache the result
else:
wifi_strength = self.wifi_strength_cache # Use cached value if available
return self.wifi_signal_icon(wifi_strength)
def wifi_signal_icon(self, strength):
if strength is None or strength <= -100:
return "signal_wifi_0_bar"
if strength > -50:
return "signal_wifi_4_bar"
if strength > -60:
return "network_wifi_3_bar"
if strength > -70:
return "network_wifi_2_bar"
return "network_wifi_1_bar"
def check_wifi_strength(self):
try:
result = subprocess.run(["iwconfig"], capture_output=True, text=True)
for line in result.stdout.splitlines():
if "Signal level" in line:
signal_level = line.split("Signal level=")[1].split(" ")[0]
return int(signal_level.strip().replace("dBm", ""))
return None
except Exception:
return None
def is_wifi_connected(self):
return any(
self._is_interface_up(interface, ["wlan", "wifi"])
for interface in psutil.net_if_addrs()
)
def is_ethernet_connected(self):
return any(
self._is_interface_up(interface, ["enp", "eth"])
for interface in psutil.net_if_addrs()
)
def _is_interface_up(self, interface, keywords):
if any(substring in interface.lower() for substring in keywords):
return psutil.net_if_stats().get(interface, {}).isup
return False
+63 -68
View File
@@ -1,88 +1,83 @@
from time import sleep
import psutil
from fabric import Fabricator
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.circularprogressbar import CircularProgressBar
from gi.repository import GLib
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from fabric.widgets.overlay import Overlay
from snippets import MaterialIcon
from fabric.widgets.circularprogressbar import CircularProgressBar
import snippets.iconss as icons
class SystemInfo(Button):
ICONS = [
("CPU", "memory"),
("Swap", "swap_horiz"),
("RAM", "memory_alt"),
("Temp", "thermometer"),
]
class SystemInfo(Box):
ICONS = {
"CPU": icons.cpu,
"RAM": icons.memory,
"Swap": icons.swap,
"Disk": icons.disk,
"Temp": icons.temp,
}
def __init__(self):
super().__init__(child=self._initialize_content())
Fabricator(
poll_from=self._refresh_system_info,
stream=True,
on_changed=lambda fab, data: self._update_ui(data),
def __init__(self, **kwargs):
super().__init__(
name="system-info",
spacing=8,
h_align="center",
v_align="fill",
visible=True,
all_visible=True,
)
def _initialize_content(self):
self.box = Box(orientation="h", name="tray")
self.progress_bars = {}
self.icons = {}
self.progress_bars = {
system: CircularProgressBar(
name="stats-progress",
line_style="round",
line_width=3,
size=28,
start_at=0,
end_at=0.5,
start_angle=0,
end_angle=180,
)
for system in self.ICONS
}
for system, icon_name in self.ICONS:
self._create_system_item(system, icon_name)
for system, icon_name in self.ICONS.items():
overlay = Overlay(
child=self.progress_bars[system],
overlays=[Label(name="stats-icons", markup=icon_name)],
)
self.add(overlay)
return self.box
GLib.timeout_add_seconds(1, self._update_system_info)
def _create_system_item(self, system, icon_name):
self.progress_bars[system] = self._create_progress_bar()
self.icons[system] = self._create_icon(icon_name)
def _update_system_info(self):
ram_usage = psutil.virtual_memory().percent
swap_usage = psutil.swap_memory().percent
disk_usage = psutil.disk_usage("/").percent
cpu = psutil.cpu_percent(interval=0)
overlay = Overlay(
child=self.progress_bars[system], overlays=[self.icons[system]]
)
self.box.pack_start(overlay, False, False, 4)
usages = {
"CPU": cpu,
"RAM": ram_usage,
"Swap": swap_usage,
"Disk": disk_usage,
"Temp": self._get_device_temperature(),
}
def _create_progress_bar(self):
return CircularProgressBar(
name="progress",
line_style="round",
line_width=3,
size=28,
start_at=0,
end_at=0.5,
# start_angle=180,
end_angle=180,
)
for system, usage in usages.items():
self.progress_bars[system].value = usage / 100.0
self.progress_bars[system].set_tooltip_text(f"{system} {usage:.1f}%")
def _create_icon(self, icon_name):
return MaterialIcon(icon_name, 16)
def _refresh_system_info(self, fab: Fabricator):
while True:
yield {
"CPU": round(psutil.cpu_percent()),
"RAM": round(psutil.virtual_memory().percent),
"Swap": round(psutil.swap_memory().percent),
"Temp": self._get_device_temperature(),
}
sleep(1)
return True
@staticmethod
def _get_device_temperature():
try:
temps = psutil.sensors_temperatures()
if "coretemp" in temps:
return round(temps["coretemp"][0].current, 1)
elif "cpu_thermal" in temps:
return round(temps["cpu_thermal"][0].current, 1)
return None
for key in ("coretemp", "cpu_thermal"):
if key in temps:
return round(temps[key][0].current, 1)
except Exception:
return None
def _update_ui(self, usages):
for system, usage in usages.items():
self.progress_bars[system].value = usage / 100
self.progress_bars[system].set_tooltip_text(f"{system} {usage}%")
pass
return None
+1 -1
View File
@@ -20,7 +20,7 @@ class PagerClient(TypedDict):
class TaskBar(Box):
def __init__(self, icon_size: int = 20, **kwargs):
def __init__(self, icon_size: int = 22, **kwargs):
super().__init__(
orientation="h",
spacing=7,
+103
View File
@@ -0,0 +1,103 @@
import json
from fabric.utils import exec_shell_command_async, get_relative_path, invoke_repeater
from fabric.widgets.box import Box
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.label import Label
from fabric.widgets.stack import Stack
import snippets.iconss as icons
from loguru import logger
from gi.repository import Gtk
from fabric.widgets.button import Button
class UpdatesWidget(Button):
"""A widget to display the number of available updates."""
def __init__(
self,
**kwargs,
):
# Initialize the button with specific name and style
super().__init__(name="button-bar", **kwargs)
self.script_file = get_relative_path("../../../config/scripts/systemupdates.sh")
self.update_level_label = Label(
name="update-label",
label="0",
visible=False, # Initially hidden
)
self.update_icon = Label(name="update-icon", markup=icons.update)
self.updated_icon = Label(name="updated-icon", markup=icons.updated)
self.update_box = CenterBox(
center_children=[self.update_icon, self.update_level_label],
)
self.updated_box = CenterBox(
center_children=[self.updated_icon],
)
self.stack = Stack()
self.stack.add_named(self.update_box, "update_box")
self.stack.add_named(self.updated_box, "updated_box")
self.stack.set_visible_child_name("update_box")
self.children = Box(
children=self.stack,
)
# Ensure the stack resizes to the current visible child
self.stack.set_homogeneous(False)
self.stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
# Connect the button press event
self.connect("button-press-event", self.on_button_press)
# Set up a repeater to call the update method at specified intervals
invoke_repeater(60000, self.update, initial_call=True)
def update_values(self, value: str):
logger.info(f"[Updates] Received update JSON: {value}")
try:
value = json.loads(value)
except json.JSONDecodeError:
logger.error("[Updates] Failed to parse update JSON")
return False
update_count = str(value["total"])
self.update_level_label.set_label(update_count)
if update_count == "0":
self.stack.set_visible_child_name("updated_box")
self.update_level_label.set_visible(False)
else:
self.stack.set_visible_child_name("update_box")
self.update_level_label.set_visible(True)
self.set_tooltip_text(value.get("tooltip", ""))
return True
def on_button_press(self, _, event):
if event.button == 1:
exec_shell_command_async(
f"{self.script_file} -arch -up",
lambda _: None,
)
self.update()
return True
else:
self.update()
def update(self):
logger.info("[Updates] Checking for updates...")
# Execute the update script asynchronously and update values
exec_shell_command_async(
f"{self.script_file} -arch",
lambda output: self.update_values(output),
)
return True
-81
View File
@@ -1,81 +0,0 @@
from fabric import Fabricator
from fabric.utils import exec_shell_command
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from snippets import MaterialIcon
from services import audio
VOLUME_ICONS = {
"muted": "volume_off",
"low": "volume_mute",
"medium-low": "volume_down",
"medium-high": "volume_up",
"high": "volume_up",
}
class VolumeIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.audio_service = audio
self.volume_icon_button = self.create_volume_icon()
self.volume_icon_button.connect("button-press-event", self.on_button_press)
self.audio_service.connect("changed", self.update_volume_status)
Fabricator(interval=1000, poll_from=self.update_volume_status)
self.children = (self.volume_icon_button,)
def create_volume_icon(self):
icon = MaterialIcon(VOLUME_ICONS["muted"], size=16)
button = Button(child=icon)
button.icon_widget = icon
return button
def on_button_press(self, widget, event):
if event.button == 1:
self.toggle_mute()
elif event.button == 3:
self.open_audio_settings()
def toggle_mute(self):
current_stream = self.audio_service.speaker
if current_stream:
current_stream.muted = not current_stream.muted
# self.update_volume_status()
def open_audio_settings(self):
exec_shell_command("pavucontrol")
def update_volume_status(self, *_):
current_stream = self.audio_service.speaker
if current_stream:
self.update_icon(current_stream)
return True
def update_icon(self, stream):
if stream.muted:
icon_name = VOLUME_ICONS["muted"]
volume_percentage = 0
tooltip_text = "Volume Muted"
else:
volume_level = stream.volume
icon_name = self.get_volume_icon(volume_level)
volume_percentage = int(volume_level)
tooltip_text = f"Volume: {volume_percentage}%"
self.volume_icon_button.icon_widget.set_label(icon_name)
self.volume_icon_button.set_tooltip_text(tooltip_text)
def get_volume_icon(self, volume_level):
if volume_level == 0:
return VOLUME_ICONS["low"]
elif volume_level <= 33:
return VOLUME_ICONS["medium-low"]
elif volume_level <= 66:
return VOLUME_ICONS["medium-high"]
else:
return VOLUME_ICONS["high"]
+149 -42
View File
@@ -9,6 +9,12 @@ from fabric.widgets.scrolledwindow import ScrolledWindow
from gi.repository import GLib, Gdk
from snippets import read_config
from fabric.widgets.image import Image
import json
import os
import re
import math
import subprocess
import webbrowser
class AppLauncher(Box):
@@ -22,11 +28,18 @@ class AppLauncher(Box):
self.launcher = kwargs["launcher"]
self.selected_index = -1
self._arranger_handler: int = 0
self._all_apps = get_desktop_applications()
self.config = read_config()
# Calculator history initialization
self.calc_history_path = os.path.expanduser("~/.cache/modus/calc.json")
if os.path.exists(self.calc_history_path):
with open(self.calc_history_path, "r") as f:
self.calc_history = json.load(f)
else:
self.calc_history = []
self.viewport = Box(name="viewport", spacing=4, orientation="v")
self.search_entry = Entry(
name="search-entry",
@@ -35,18 +48,13 @@ class AppLauncher(Box):
on_activate=lambda entry, *_: self.on_search_entry_activate(
entry.get_text()
),
on_key_press_event=self.on_search_entry_key_press, # Handle key presses
)
self.scrolled_window = ScrolledWindow(
name="scrolled-window",
spacing=10,
h_scrollbar_policy="never",
v_scrollbar_policy="never",
child=self.viewport,
on_key_press_event=self.on_search_entry_key_press,
)
self.scrolled_window = None
self.header_box = Box(
name="header_box",
name="header-box",
orientation="h",
children=[self.search_entry],
)
@@ -55,41 +63,46 @@ class AppLauncher(Box):
name="launcher-box",
h_expand=True,
orientation="v",
children=[
self.header_box,
self.scrolled_window,
],
children=[self.header_box],
)
self.resize_viewport()
self.add(self.launcher_box)
self.show_all()
def close_launcher(self, *_):
self.viewport.children = []
self.selected_index = -1 # Reset selection
self.selected_index = -1
self.launcher.close()
def open_launcher(self):
self._all_apps = get_desktop_applications()
self.arrange_viewport()
self.show_all()
self.arrange_viewport("")
def arrange_viewport(self, query: str = ""):
if query.startswith(":"): # Don't show ScrolledWindow for web search
self.destroy_scrolled_window()
self.launcher.dashboard.hide()
return
if query.startswith("="):
self.show_scrolled_window()
# In calculator mode, update history view once (not per keystroke)
self.update_calculator_viewport()
self.launcher.dashboard.hide()
return
remove_handler(self._arranger_handler) if self._arranger_handler else None
self.viewport.children = []
self.selected_index = -1
if query.strip():
self.launcher.dashboard.hide()
self.show_scrolled_window()
else:
self.launcher.dashboard.show()
if not query.strip():
self.destroy_scrolled_window()
return False
filtered_apps = iter(
filtered_apps_iter = iter(
sorted(
[
app
@@ -100,19 +113,41 @@ class AppLauncher(Box):
+ (" " + app.name + " ")
+ (app.generic_name or "")
).casefold()
][:6],
],
key=lambda app: (app.display_name or "").casefold(),
)
)
should_resize = operator.length_hint(filtered_apps) == len(self._all_apps)
should_resize = operator.length_hint(filtered_apps_iter) == len(self._all_apps)
self._arranger_handler = idle_add(
lambda apps_iter: self.add_next_application(apps_iter)
or self.handle_arrange_complete(should_resize, query),
filtered_apps,
filtered_apps_iter,
pin=True,
)
def show_scrolled_window(self):
if self.scrolled_window is None:
self.scrolled_window = ScrolledWindow(
name="scrolled-window",
spacing=10,
min_content_size=(-1, -1),
h_scrollbar_policy="never",
child=self.viewport,
v_expand=True,
)
self.launcher_box.add(self.scrolled_window)
self.scrolled_window.show_all()
self.get_style_context().add_class("applauncher")
def destroy_scrolled_window(self):
if hasattr(self, "scrolled_window") and self.scrolled_window:
# self.scrolled_window.destroy()
self.launcher_box.remove(self.scrolled_window)
self.scrolled_window = None
self.get_style_context().remove_class("applauncher")
def handle_arrange_complete(self, should_resize, query):
if should_resize:
self.resize_viewport()
@@ -128,12 +163,6 @@ class AppLauncher(Box):
self.viewport.add(self.bake_application_slot(app))
return True
def resize_viewport(self):
self.scrolled_window.set_min_content_width(
self.viewport.get_allocation().width # type: ignore
)
return False
def bake_application_slot(self, app: DesktopApp, **kwargs) -> Button:
button = Button(
name="launcher-app",
@@ -160,13 +189,12 @@ class AppLauncher(Box):
return button
def update_selection(self, new_index: int):
# Unselect current
if self.selected_index != -1 and self.selected_index < len(
self.viewport.get_children()
):
current_button = self.viewport.get_children()[self.selected_index]
current_button.get_style_context().remove_class("selected")
# Select new
if new_index != -1 and new_index < len(self.viewport.get_children()):
new_button = self.viewport.get_children()[new_index]
new_button.get_style_context().add_class("selected")
@@ -180,41 +208,49 @@ class AppLauncher(Box):
adj = self.scrolled_window.get_vadjustment()
alloc = button.get_allocation()
if alloc.height == 0:
return False # Retry if allocation isn't ready
return False
y = alloc.y
height = alloc.height
page_size = adj.get_page_size()
current_value = adj.get_value()
# Calculate visible boundaries
visible_top = current_value
visible_bottom = current_value + page_size
if y < visible_top:
# Item above viewport - align to top
adj.set_value(y)
elif y + height > visible_bottom:
# Item below viewport - align to bottom
new_value = y + height - page_size
adj.set_value(new_value)
# No action if already fully visible
return False
GLib.idle_add(scroll)
def on_search_entry_activate(self, text):
commands = self.config.get("commands", {})
if text.startswith(":i "): # Detecting ':i' search
search_query = text[3:].strip() # Extract query after ':i '
if search_query:
url = f"https://www.google.com/search?q={search_query}"
webbrowser.open(url)
self.close_launcher()
return
if text.startswith("="):
# If in calculator mode and no history item is selected, evaluate new expression.
if self.selected_index == -1:
self.evaluate_calculator_expression(text)
return
if text in commands:
command = commands[text]
eval(command, {"launcher": self.launcher}) # Dynamically execute command
exec(command, {"launcher": self.launcher})
else:
children = self.viewport.get_children()
if children:
# Only activate if we have selection or non-empty query
if text.strip() == "" and self.selected_index == -1:
return # Prevent accidental activation when empty
return
selected_index = self.selected_index if self.selected_index != -1 else 0
if 0 <= selected_index < len(children):
children[selected_index].clicked()
@@ -236,10 +272,81 @@ class AppLauncher(Box):
children = self.viewport.get_children()
if not children:
return
# Allow starting selection from nothing when empty
if self.selected_index == -1 and delta == 1:
new_index = 0
else:
new_index = self.selected_index + delta
new_index = max(0, min(new_index, len(children) - 1))
self.update_selection(new_index)
def save_calc_history(self):
with open(self.calc_history_path, "w") as f:
json.dump(self.calc_history, f)
def evaluate_calculator_expression(self, text: str):
# Remove the '=' prefix and extra spaces
expr = text.lstrip("=").strip()
if not expr:
return
# Replace operators: '^' -> '**', and '×' -> '*'
expr = expr.replace("^", "**").replace("×", "*")
# Replace factorial: e.g. 5! -> math.factorial(5)
expr = re.sub(r"(\d+)!", r"math.factorial(\1)", expr)
# Replace brackets: allow [] and {} as ()
for old, new in [("[", "("), ("]", ")"), ("{", "("), ("}", ")")]:
expr = expr.replace(old, new)
try:
result = eval(expr, {"__builtins__": None, "math": math})
except Exception as e:
result = f"Error: {e}"
# Prepend to history (newest first)
self.calc_history.insert(0, f"{text} => {result}")
self.save_calc_history()
self.update_calculator_viewport()
def update_calculator_viewport(self):
self.viewport.children = []
for item in self.calc_history:
btn = self.create_calc_history_button(item)
self.viewport.add(btn)
# Remove resetting selected_index unconditionally so that a highlighted result isn't lost.
# Optionally, only reset if the input is not more than "=".
# if self.search_entry.get_text().strip() != "=":
# self.selected_index = -1
def create_calc_history_button(self, text: str) -> Button:
btn = Button(
name="app-slot-button", # reuse existing CSS styling
child=Box(
name="calc-slot-box",
orientation="h",
spacing=10,
children=[
Label(
name="calc-label",
label=text,
ellipsization="end",
v_align="center",
h_align="center",
),
],
),
tooltip_text=text,
on_clicked=lambda *_: self.copy_text_to_clipboard(text),
)
return btn
def copy_text_to_clipboard(self, text: str):
# Split the text on "=>" and copy only the result part if available
parts = text.split("=>", 1)
copy_text = parts[1].strip() if len(parts) > 1 else text
try:
subprocess.run(["wl-copy"], input=copy_text.encode(), check=True)
except subprocess.CalledProcessError as e:
print(f"Clipboard copy failed: {e}")
def delete_selected_calc_history(self):
if self.selected_index != -1 and self.selected_index < len(self.calc_history):
del self.calc_history[self.selected_index]
self.save_calc_history()
self.update_calculator_viewport()
+38 -34
View File
@@ -5,8 +5,7 @@ from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.scrolledwindow import ScrolledWindow
from fabric.bluetooth import BluetoothClient, BluetoothDevice
from snippets import MaterialIcon
# from .buttons import
import snippets.iconss as icons
class BluetoothDeviceSlot(CenterBox):
@@ -18,7 +17,9 @@ class BluetoothDeviceSlot(CenterBox):
"notify::closed", lambda *_: self.device.closed and self.destroy()
)
self.connection_icon = MaterialIcon("bluetooth_disabled", 12)
self.connection_label = Label(
name="bluetooth-connection", markup=icons.bluetooth_disconnected
)
self.connect_button = Button(
name="bluetooth-connect",
label="Connect",
@@ -29,9 +30,9 @@ class BluetoothDeviceSlot(CenterBox):
Box(
spacing=8,
children=[
Image(icon_name=device.icon_name + "-symbolic", size=32),
Image(icon_name=f"{device.icon_name}-symbolic", size=32),
Label(label=device.name),
self.connection_icon,
self.connection_label,
],
)
]
@@ -40,10 +41,11 @@ class BluetoothDeviceSlot(CenterBox):
self.device.emit("changed") # to update display status
def on_changed(self, *_):
self.connection_icon.set_label(
"bluetooth" if self.device.connected else "bluetooth_disabled"
self.connection_label.set_markup(
icons.bluetooth_connected
if self.device.connected
else icons.bluetooth_disconnected
)
if self.device.connecting:
self.connect_button.set_label(
"Connecting..." if not self.device.connecting else "Disconnecting..."
@@ -63,38 +65,37 @@ class BluetoothConnections(Box):
orientation="vertical",
**kwargs,
)
self.launcher = kwargs["launcher"]
self.bluetooth_button = self.launcher.dashboard.bluetooth_button
self.bt_status_text = self.bluetooth_button.bluetooth_status_text
self.bt_status_button = self.bluetooth_button.bluetooth_status_button
self.bt_icon = self.bluetooth_button.bluetooth_icon
self.bt_label = self.bluetooth_button.bluetooth_label
self.bt_menu_button = self.bluetooth_button.bluetooth_menu_button
self.bt_menu_label = self.bluetooth_button.bluetooth_menu_label
self.buttons = self.launcher.dashboard.bluetooth_button
self.bt_status_text = self.buttons.bluetooth_status_text
self.bt_status_button = self.buttons.bluetooth_status_button
self.bt_icon = self.buttons.bluetooth_icon
self.bt_label = self.buttons.bluetooth_label
self.bt_menu_button = self.buttons.bluetooth_menu_button
self.bt_menu_label = self.buttons.bluetooth_menu_label
self.client = BluetoothClient(on_device_added=self.on_device_added)
self.toggle_icon = MaterialIcon("bluetooth_disabled")
self.scan_icon = MaterialIcon("stop")
self.scan_button = Button(
name="bluetooth-scan",
child=self.scan_icon,
tooltip_text="scan",
child=Label(name="bluetooth-scan-icon", markup=icons.scan_stop),
on_clicked=lambda *_: self.client.toggle_scan(),
)
self.toggle_button = Button(
name="bluetooth-toggle",
tooltip_text="bluetooth-toggle",
child=self.toggle_icon,
child=Label(name="bluetooth-toggle-icon", markup=icons.bluetooth_off),
on_clicked=lambda *_: self.client.toggle_power(),
)
self.client.connect("notify::enabled", lambda *_: self.status_label())
self.client.connect(
"notify::enabled",
lambda *_: self.status_label(),
)
self.client.connect(
"notify::scanning",
lambda *_: self.scan_icon.set_label(
"stop" if self.client.scanning else "bluetooth_searching"
lambda *_: self.scan_button.get_child().set_markup(
icons.scan_stop if self.client.scanning else icons.scan
),
)
@@ -111,14 +112,16 @@ class BluetoothConnections(Box):
Label(name="bluetooth-section", label="Paired"),
ScrolledWindow(
name="bluetooth-paired",
v_expand=True,
min_content_size=(-1, -1),
child=self.paired_box,
v_expand=True,
),
Label(name="bluetooth-section", label="Available"),
ScrolledWindow(
name="bluetooth-paired",
v_expand=True,
name="bluetooth-available",
min_content_size=(-1, -1),
child=self.available_box,
v_expand=True,
),
]
@@ -130,7 +133,7 @@ class BluetoothConnections(Box):
def status_label(self):
print(self.client.enabled)
if self.client.enabled:
self.toggle_icon.set_label("bluetooth")
self.toggle_button.get_child().set_markup(icons.bluetooth)
self.bt_status_text.set_label("Enabled")
for i in [
self.bt_status_button,
@@ -141,9 +144,9 @@ class BluetoothConnections(Box):
self.bt_menu_label,
]:
i.remove_style_class("disabled")
self.bt_icon.set_label("bluetooth")
self.bt_icon.set_markup(icons.bluetooth)
else:
self.toggle_icon.set_label("bluetooth_disabled")
self.toggle_button.get_child().set_markup(icons.bluetooth_off)
self.bt_status_text.set_label("Disabled")
for i in [
self.bt_status_button,
@@ -154,7 +157,7 @@ class BluetoothConnections(Box):
self.bt_menu_label,
]:
i.add_style_class("disabled")
self.bt_icon.set_label("bluetooth_disabled")
self.bt_icon.set_markup(icons.bluetooth_off)
def on_device_added(self, client: BluetoothClient, address: str):
if not (device := client.get_device(address)):
@@ -162,5 +165,6 @@ class BluetoothConnections(Box):
slot = BluetoothDeviceSlot(device)
if device.paired:
return self.paired_box.add(slot)
return self.available_box.add(slot)
self.paired_box.add(slot)
else:
self.available_box.add(slot)
-396
View File
@@ -1,396 +0,0 @@
import subprocess
from fabric.widgets.box import Box
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.label import Label
from fabric.widgets.button import Button
from fabric.utils.helpers import exec_shell_command_async, exec_shell_command
import gi
from gi.repository import Gtk, Gdk, GLib # Added Gdk import
from fabric.utils import get_relative_path
gi.require_version("Gtk", "3.0")
from snippets import MaterialIcon
def add_hover_cursor(widget):
# Add enter/leave events to change the cursor
widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK)
widget.connect(
"enter-notify-event",
lambda w, e: w.get_window().set_cursor(
Gdk.Cursor.new_from_name(w.get_display(), "pointer")
)
if w.get_window()
else None,
)
widget.connect(
"leave-notify-event",
lambda w, e: w.get_window().set_cursor(None) if w.get_window() else None,
)
class NetworkButton(Box):
def __init__(self):
self.network_icon = MaterialIcon(
"wifi",
)
self.network_icon.add_style_class("network-icon")
self.network_label = Label(
name="network-label",
label="Wi-Fi",
justification="left",
)
self.network_label_box = Box(children=[self.network_label, Box(h_expand=True)])
self.network_ssid = Label(
name="network-ssid",
label="Hello_World!!",
justification="left",
)
self.network_ssid_box = Box(children=[self.network_ssid, Box(h_expand=True)])
self.network_text = Box(
name="network-text",
orientation="v",
h_align="start",
v_align="center",
children=[self.network_label_box, self.network_ssid_box],
)
self.network_status_box = Box(
h_align="start",
v_align="center",
spacing=10,
children=[self.network_icon, self.network_text],
)
self.network_status_button = Button(
name="network-status-button",
h_expand=True,
child=self.network_status_box,
)
add_hover_cursor(self.network_status_button) # <-- Added hover
self.network_menu_label = MaterialIcon("chevron_right")
self.network_menu_button = Button(
name="network-menu-button",
child=self.network_menu_label,
)
add_hover_cursor(self.network_menu_button) # <-- Added hover
super().__init__(
name="network-button",
orientation="h",
h_align="fill",
v_align="fill",
h_expand=True,
v_expand=True,
spacing=0,
children=[self.network_status_button, self.network_menu_button],
)
class BluetoothButton(Box):
def __init__(self, launcher):
super().__init__(
name="bluetooth-button",
orientation="h",
h_align="fill",
v_align="fill",
h_expand=True,
v_expand=True,
)
self.launcher = launcher
self.bluetooth_icon = MaterialIcon("bluetooth")
self.bluetooth_label = Label(
name="bluetooth-label",
label="Bluetooth",
justification="left",
)
self.bluetooth_label_box = Box(
children=[self.bluetooth_label, Box(h_expand=True)]
)
self.bluetooth_status_text = Label(
name="bluetooth-status",
label="Disabled",
justification="left",
)
self.bluetooth_status_box = Box(
children=[self.bluetooth_status_text, Box(h_expand=True)]
)
self.bluetooth_text = Box(
orientation="v",
h_align="start",
v_align="center",
children=[self.bluetooth_label_box, self.bluetooth_status_box],
)
self.bluetooth_status_container = Box(
h_align="start",
v_align="center",
spacing=10,
children=[self.bluetooth_icon, self.bluetooth_text],
)
self.bluetooth_status_button = Button(
name="bluetooth-status-button",
h_expand=True,
child=self.bluetooth_status_container,
on_clicked=lambda *_: self.launcher.bluetooth.client.toggle_power(),
)
add_hover_cursor(self.bluetooth_status_button) # <-- Added hover
self.bluetooth_menu_label = MaterialIcon("chevron_right")
self.bluetooth_menu_button = Button(
name="bluetooth-menu-button",
on_clicked=lambda *_: self.launcher.open("bluetooth"),
child=self.bluetooth_menu_label,
)
add_hover_cursor(self.bluetooth_menu_button) # <-- Added hover
self.add(self.bluetooth_status_button)
self.add(self.bluetooth_menu_button)
class NightModeButton(Button):
def __init__(self):
self.night_mode_icon = MaterialIcon("mode_night")
self.night_mode_label = Label(
name="night-mode-label",
label="Night Mode",
justification="left",
)
self.night_mode_label_box = Box(
children=[self.night_mode_label, Box(h_expand=True)]
)
self.night_mode_status = Label(
name="night-mode-status",
label="Enabled",
justification="left",
)
self.night_mode_status_box = Box(
children=[self.night_mode_status, Box(h_expand=True)]
)
self.night_mode_text = Box(
name="night-mode-text",
orientation="v",
h_align="start",
v_align="center",
children=[self.night_mode_label_box, self.night_mode_status_box],
)
self.night_mode_box = Box(
h_align="start",
v_align="center",
spacing=10,
children=[self.night_mode_icon, self.night_mode_text],
)
super().__init__(
name="night-mode-button",
h_expand=True,
child=self.night_mode_box,
on_clicked=self.toggle_hyprsunset,
)
add_hover_cursor(self) # <-- Added hover
self.widgets = [
self,
self.night_mode_label,
self.night_mode_status,
self.night_mode_icon,
]
self.check_hyprsunset()
def toggle_hyprsunset(self, *args):
"""
Toggle the 'hyprsunset' process:
- If running, kill it and mark as 'Disabled'.
- If not running, start it and mark as 'Enabled'.
"""
try:
subprocess.check_output(["pgrep", "hyprsunset"])
exec_shell_command_async("pkill hyprsunset")
self.night_mode_status.set_label("Disabled")
for widget in self.widgets:
widget.add_style_class("disabled")
except subprocess.CalledProcessError:
exec_shell_command_async("hyprsunset -t 3500")
self.night_mode_status.set_label("Enabled")
for widget in self.widgets:
widget.remove_style_class("disabled")
def check_hyprsunset(self, *args):
"""
Update the button state based on whether hyprsunset is running.
"""
try:
subprocess.check_output(["pgrep", "hyprsunset"])
self.night_mode_status.set_label("Enabled")
for widget in self.widgets:
widget.remove_style_class("disabled")
except subprocess.CalledProcessError:
self.night_mode_status.set_label("Disabled")
for widget in self.widgets:
widget.add_style_class("disabled")
class CaffeineButton(Button):
def __init__(self):
self.caffeine_icon = MaterialIcon("coffee")
self.caffeine_label = Label(
name="caffeine-label",
label="Caffeine",
justification="left",
)
self.caffeine_label_box = Box(
children=[self.caffeine_label, Box(h_expand=True)]
)
self.caffeine_status = Label(
name="caffeine-status",
label="Enabled",
justification="left",
)
self.caffeine_status_box = Box(
children=[self.caffeine_status, Box(h_expand=True)]
)
self.caffeine_text = Box(
name="caffeine-text",
orientation="v",
h_align="start",
v_align="center",
children=[self.caffeine_label_box, self.caffeine_status_box],
)
self.caffeine_box = Box(
h_align="start",
v_align="center",
spacing=10,
children=[self.caffeine_icon, self.caffeine_text],
)
super().__init__(
name="caffeine-button",
h_expand=True,
child=self.caffeine_box,
on_clicked=self.toggle_wlinhibit,
)
add_hover_cursor(self) # <-- Added hover
self.widgets = [
self,
self.caffeine_label,
self.caffeine_status,
self.caffeine_icon,
]
self.check_wlinhibit()
def toggle_wlinhibit(self, *args):
"""
Toggle the 'wlinhibit' process:
- If running, kill it and mark as 'Disabled' (add 'disabled' class).
- If not running, start it and mark as 'Enabled' (remove 'disabled' class).
"""
try:
subprocess.check_output(["pgrep", "wlinhibit"])
exec_shell_command_async("pkill wlinhibit")
self.caffeine_status.set_label("Disabled")
for i in self.widgets:
i.add_style_class("disabled")
except subprocess.CalledProcessError:
exec_shell_command_async("wlinhibit")
self.caffeine_status.set_label("Enabled")
for i in self.widgets:
i.remove_style_class("disabled")
def check_wlinhibit(self, *args):
try:
subprocess.check_output(["pgrep", "wlinhibit"])
self.caffeine_status.set_label("Enabled")
for i in self.widgets:
i.remove_style_class("disabled")
except subprocess.CalledProcessError:
self.caffeine_status.set_label("Disabled")
for i in self.widgets:
i.add_style_class("disabled")
class DarkModeButton(Button):
def __init__(self):
self.dark_mode_icon = MaterialIcon("contrast")
self.dark_mode_icon.add_style_class("iconss")
self.dark_mode_label = Label(
name="dark-mode-label", label="Dark Mode", justification="left"
)
self.dark_mode_label_box = Box(
children=[self.dark_mode_label, Box(h_expand=True)]
)
self.dark_mode_status = Label(
name="dark-mode-status", label="Enabled", justification="left"
)
self.dark_mode_status_box = Box(
children=[self.dark_mode_status, Box(h_expand=True)]
)
self.dark_mode_text = Box(
name="dark-mode-text",
orientation="v",
h_align="start",
v_align="center",
children=[self.dark_mode_label_box, self.dark_mode_status_box],
)
self.dark_mode_box = Box(
h_align="start",
v_align="center",
spacing=10,
children=[self.dark_mode_icon, self.dark_mode_text],
)
super().__init__(
name="dark-mode-button",
h_expand=True,
child=self.dark_mode_box,
on_clicked=self.toggle_darkmode,
)
add_hover_cursor(self)
self.widgets = [
self,
self.dark_mode_label,
self.dark_mode_status,
self.dark_mode_icon,
]
self.check_darkmode()
def toggle_darkmode(self, *_):
current_state = self.check_darkmode()
new_mode = "dark" if not current_state else "light"
command = f"bash {get_relative_path('../../../config/scripts/dark-theme.sh')} --set {new_mode}"
GLib.spawn_command_line_async(command)
self.dark_mode_status.set_label("Enabled" if new_mode == "dark" else "Disabled")
def check_darkmode(self):
result = exec_shell_command(
"gsettings get org.gnome.desktop.interface color-scheme"
)
return result.strip().replace("'", "") == "prefer-dark"
class Buttons(Gtk.Grid):
def __init__(self, **kwargs):
super().__init__(name="buttons-grid")
self.set_row_homogeneous(True)
self.set_column_homogeneous(True)
self.set_row_spacing(8)
self.set_column_spacing(8)
self.set_vexpand(True) # Prevent vertical expansion
self.launcher = kwargs["launcher"]
# Instantiate each button
self.network_button = NetworkButton()
self.bluetooth_button = BluetoothButton(self.launcher)
self.night_mode_button = NightModeButton()
self.caffeine_button = CaffeineButton()
self.dark_mode_button = DarkModeButton()
# Attach buttons into the grid (one row, four columns)
self.attach(self.network_button, 0, 0, 1, 1)
self.attach(self.bluetooth_button, 1, 0, 1, 1)
self.attach(self.dark_mode_button, 2, 0, 1, 1)
self.attach(self.night_mode_button, 0, 1, 1, 1)
self.attach(self.caffeine_button, 1, 1, 1, 1)
self.show_all()
+3 -3
View File
@@ -1,11 +1,11 @@
import calendar
from datetime import datetime
import gi
from snippets import MaterialIcon
from fabric.widgets.label import Label
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
import snippets.iconss as icons
class Calendar(Gtk.Box):
@@ -27,7 +27,7 @@ class Calendar(Gtk.Box):
self.prev_month_button = Gtk.Button(
name="prev-month-button",
child=MaterialIcon("chevron_left"),
child=Label(name="month-button-label", markup=icons.chevron_left)
)
self.prev_month_button.connect("clicked", self.on_prev_month_clicked)
self.header.pack_start(self.prev_month_button, False, False, 0)
@@ -37,7 +37,7 @@ class Calendar(Gtk.Box):
self.next_month_button = Gtk.Button(
name="next-month-button",
child=MaterialIcon("chevron_right"),
child=Label(name="month-button-label", markup=icons.chevron_right)
)
self.next_month_button.connect("clicked", self.on_next_month_clicked)
self.header.pack_start(self.next_month_button, False, False, 0)
+33 -43
View File
@@ -1,16 +1,14 @@
import os
import subprocess
from typing import List
from fabric import Fabricator
from fabric.utils import remove_handler
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.entry import Entry
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
from gi.repository import GLib
from snippets import MaterialIcon
import snippets.iconss as icons
class Cliphist(Box):
@@ -25,30 +23,30 @@ class Cliphist(Box):
self._arranger_handler = 0
self.cliphist_manager = CliphistManager(self)
self.viewport = None
self.launcher = kwargs["launcher"]
self.search_entry = Entry(
name="search-entry",
h_expand=True,
notify_text=lambda entry, *_: self.handle_search_input(entry.get_text()),
on_activate=lambda entry, *_: self.handle_search_input(entry.get_text()),
)
self.toggle_button = Button(
child=MaterialIcon("delete_forever"),
name="clear-launcher-button",
on_clicked=self.on_button_clicked,
on_activate=lambda entry, *_: self.handle_search(entry.get_text()),
)
self.header_box = Box(
name="header-box",
spacing=10,
orientation="h",
children=[self.search_entry, self.toggle_button],
children=[
self.search_entry,
Button(
name="clear-button",
child=Label(name="cliphist-clear-button", markup=icons.clear),
tooltip_text="Clear Clipboard History",
on_clicked=self.handle_clear_history,
),
],
)
self.launcher_box = Box(
name="cliphist-history",
name="cliphist-launcher-box",
spacing=10,
orientation="v",
h_expand=True,
@@ -57,32 +55,31 @@ class Cliphist(Box):
self.add(self.launcher_box)
def on_button_clicked(self, *_):
GLib.spawn_command_line_async("cliphist wipe")
self.cliphist_manager.update_cliphist_history()
def open_launcher(self):
if not self.viewport:
self.viewport = Box(name="viewport", spacing=4, orientation="v")
self.scrolled_window = ScrolledWindow(
name="scrolled-window",
spacing=10,
min_content_size=(-1, -1),
h_scrollbar_policy="never",
# v_scrollbar_policy="never",
child=self.viewport,
)
self.launcher_box.add(self.scrolled_window)
self.viewport.children = []
self.cliphist_manager.arrange_viewport()
self.cliphist_manager.update_cliphist_history()
self.viewport.show()
self.search_entry.grab_focus()
def close_launcher(self):
self.launcher.close()
def handle_search(self, text: str):
self.cliphist_manager.arrange_viewport(query=text)
def handle_search_input(self, text: str):
self.cliphist_manager.arrange_viewport(text)
def handle_clear_history(self, *_):
GLib.spawn_command_line_async("cliphist wipe")
self.cliphist_manager.update_cliphist_history()
class CliphistManager:
@@ -121,7 +118,7 @@ class CliphistManager:
["cliphist", "decode", clip_id], capture_output=True, check=True
)
subprocess.run(["wl-copy"], input=decode_result.stdout, check=True)
self.launcher.close_launcher()
self.launcher.open_launcher()
except subprocess.CalledProcessError as e:
print(f"Error copying clip {clip_id}: {e}")
@@ -133,22 +130,25 @@ class CliphistManager:
decode_result = subprocess.run(
["cliphist", "decode", clip_id], capture_output=True, check=True
)
with open(output_file, "wb") as f:
f.write(decode_result.stdout)
return output_file
except (subprocess.CalledProcessError, IOError) as e:
print(f"Error saving image file for clip {clip_id}: {e}")
return ""
def update_cliphist_history(self):
self.cliphist_history = self.get_clip_history()
self.arrange_viewport()
def query_clips(self, query: str = "") -> List[dict]:
history = self.get_clip_history()
if not query.strip():
return history
return [item for item in history if query.lower() in item["content"].lower()]
return self.cliphist_history
return [
clip
for clip in self.cliphist_history
if query.lower() in clip["content"].lower()
]
def bake_clip_slot(self, item: dict) -> Button:
if "[[ binary data" in item["content"].lower():
@@ -163,19 +163,17 @@ class CliphistManager:
name="clip-img-item",
on_clicked=lambda _, clip_id=item["id"]: self.copy_clip(clip_id),
)
button.set_style(
f"background-image: url('{output_file}'); background-size: cover; background-position: center;"
)
return button
return None
def _create_text_button(self, item: dict) -> Button:
return Button(
child=Label(
label=item["content"][:55]
+ ("..." if len(item["content"]) > 55 else ""),
label=item["content"][:35]
+ ("..." if len(item["content"]) > 35 else ""),
h_expand=True,
v_align="center",
h_align="start",
@@ -184,18 +182,10 @@ class CliphistManager:
name="clip-item",
)
def update_cliphist_history(self):
self.cliphist_history = self.get_clip_history()
if self.launcher.viewport:
GLib.idle_add(self.arrange_viewport)
def arrange_viewport(self, query: str = ""):
if not self.launcher.viewport:
return
if self.launcher._arranger_handler:
remove_handler(self.launcher._arranger_handler)
self.launcher.viewport.children = []
filtered_clips = self.query_clips(query)
+28 -102
View File
@@ -9,7 +9,7 @@ from gi.repository import Gtk, Gdk, GLib # Added Gdk import
from fabric.utils import get_relative_path
gi.require_version("Gtk", "3.0")
from snippets import MaterialIcon
import snippets.iconss as icons
def add_hover_cursor(widget):
@@ -31,8 +31,9 @@ def add_hover_cursor(widget):
class NetworkButton(Box):
def __init__(self):
self.network_icon = MaterialIcon(
"wifi",
self.network_icon = Label(
name="network-icon",
markup=icons.wifi,
)
self.network_label = Label(
name="network-label",
@@ -66,7 +67,10 @@ class NetworkButton(Box):
)
add_hover_cursor(self.network_status_button) # <-- Added hover
self.network_menu_label = MaterialIcon("chevron_right")
self.network_menu_label = Label(
name="network-menu-label",
markup=icons.chevron_right,
)
self.network_menu_button = Button(
name="network-menu-button",
child=self.network_menu_label,
@@ -97,7 +101,10 @@ class BluetoothButton(Box):
)
self.launcher = launcher
self.bluetooth_icon = MaterialIcon("bluetooth")
self.bluetooth_icon = Label(
name="bluetooth-icon",
markup=icons.bluetooth,
)
self.bluetooth_label = Label(
name="bluetooth-label",
label="Bluetooth",
@@ -133,8 +140,10 @@ class BluetoothButton(Box):
on_clicked=lambda *_: self.launcher.bluetooth.client.toggle_power(),
)
add_hover_cursor(self.bluetooth_status_button) # <-- Added hover
self.bluetooth_menu_label = MaterialIcon("chevron_right")
self.bluetooth_menu_label = Label(
name="bluetooth-menu-label",
markup=icons.chevron_right,
)
self.bluetooth_menu_button = Button(
name="bluetooth-menu-button",
on_clicked=lambda *_: self.launcher.open("bluetooth"),
@@ -148,7 +157,10 @@ class BluetoothButton(Box):
class NightModeButton(Button):
def __init__(self):
self.night_mode_icon = MaterialIcon("mode_night")
self.night_mode_icon = Label(
name="night-mode-icon",
markup=icons.night,
)
self.night_mode_label = Label(
name="night-mode-label",
label="Night Mode",
@@ -230,7 +242,10 @@ class NightModeButton(Button):
class CaffeineButton(Button):
def __init__(self):
self.caffeine_icon = MaterialIcon("coffee")
self.caffeine_icon = Label(
name="caffeine-icon",
markup=icons.coffee,
)
self.caffeine_label = Label(
name="caffeine-label",
label="Caffeine",
@@ -308,7 +323,10 @@ class CaffeineButton(Button):
class DarkModeButton(Button):
def __init__(self):
self.dark_mode_icon = MaterialIcon("contrast")
self.dark_mode_icon = Label(
name="dark-mode-icon",
markup=icons.darkmode,
)
self.dark_mode_label = Label(
name="dark-mode-label", label="Dark Mode", justification="left"
)
@@ -384,96 +402,6 @@ class DarkModeButton(Button):
return is_dark_mode
#
# class PowerProfileButton(Button):
# def __init__(self):
# self.power_profile_icon = MaterialIcon("local_fire_department")
# self.power_profile_label = Label(
# name="power-profile-label", label="Power Profile", justification="left"
# )
# self.power_profile_label_box = Box(
# children=[self.power_profile_label, Box(h_expand=True)]
# )
# self.power_profile_status = Label(
# name="power-profile-status", label="Loading...", justification="left"
# )
# self.power_profile_status_box = Box(
# children=[self.power_profile_status, Box(h_expand=True)]
# )
# self.power_profile_text = Box(
# name="power-profile-text",
# orientation="v",
# h_align="start",
# v_align="center",
# children=[self.power_profile_label_box, self.power_profile_status_box],
# )
# self.power_profile_box = Box(
# h_align="start",
# v_align="center",
# spacing=10,
# children=[self.power_profile_icon, self.power_profile_text],
# )
#
# super().__init__(
# name="power-profile-button",
# h_expand=True,
# child=self.power_profile_box,
# on_clicked=self.toggle_power_profile,
# )
# add_hover_cursor(self)
#
# self.widgets = [
# self,
# self.power_profile_label,
# self.power_profile_status,
# self.power_profile_icon,
# ]
#
# # **Delay the profile check to allow system services to start**
# GLib.timeout_add(1000, self.check_power_profile)
#
# def toggle_power_profile(self, *_):
# current_profile = self.check_power_profile()
# if current_profile == "balanced":
# self.set_power_profile("performance")
# elif current_profile == "performance":
# self.set_power_profile("power-saver")
# else:
# self.set_power_profile("balanced")
#
# def check_power_profile(self):
# try:
# result = exec_shell_command("powerprofilesctl get")
# if isinstance(result, str):
# current_profile = result.strip().lower()
# self.power_profile_status.set_label(current_profile.capitalize())
# self.update_button_style(current_profile)
# return current_profile
# except Exception as e:
# print(f"Error checking power profile: {e}")
# self.power_profile_status.set_label("Balanced") # Fallback
# return "balanced"
#
# def set_power_profile(self, profile):
# exec_shell_command_async(f"powerprofilesctl set {profile}")
# self.power_profile_status.set_label(profile.capitalize())
# self.update_button_style(profile)
#
# def update_button_style(self, profile):
# if profile == "balanced":
# self.power_profile_icon.set_label("balance")
# self.add_style_class("balanced-profile")
# self.power_profile_label.add_style_class("disabled")
# else:
# self.remove_style_class("balanced-profile")
#
# if profile == "performance":
# self.power_profile_icon.set_label("local_fire_department")
# elif profile == "power-saver":
# self.power_profile_icon.set_label("data_saver_on")
#
class Dashboard(Gtk.Grid):
def __init__(self, **kwargs):
super().__init__(name="buttons-grid")
@@ -491,7 +419,6 @@ class Dashboard(Gtk.Grid):
self.night_mode_button = NightModeButton()
self.caffeine_button = CaffeineButton()
self.dark_mode_button = DarkModeButton()
# self.power_profile_button = PowerProfileButton()
# Attach buttons into the grid (one row, four columns)
self.attach(self.network_button, 0, 0, 1, 1)
@@ -499,6 +426,5 @@ class Dashboard(Gtk.Grid):
self.attach(self.dark_mode_button, 2, 0, 1, 1)
self.attach(self.night_mode_button, 0, 1, 1, 1)
self.attach(self.caffeine_button, 1, 1, 1, 1)
# self.attach(self.power_profile_button, 2, 1, 1, 1)
self.show_all()
+25 -24
View File
@@ -1,14 +1,14 @@
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.button import Button
from gi.repository import GLib
from snippets import MaterialIcon
from fabric.utils.helpers import exec_shell_command_async
import snippets.iconss as icons
class PowerMenu(Box):
def __init__(self, **kwargs):
super().__init__(
name="powermenu",
name="power-menu",
orientation="h",
spacing=10,
v_align="center",
@@ -22,32 +22,32 @@ class PowerMenu(Box):
self.launcher = kwargs["launcher"]
self.btn_lock = Button(
name="powermenu-button",
child=MaterialIcon("lock", 45),
name="power-menu-button",
child=Label(name="button-label", markup=icons.lock),
on_clicked=self.lock,
)
self.btn_suspend = Button(
name="powermenu-button",
child=MaterialIcon("sleep", 45),
name="power-menu-button",
child=Label(name="button-label", markup=icons.suspend),
on_clicked=self.suspend,
)
self.btn_logout = Button(
name="powermenu-button",
child=MaterialIcon("logout", 45),
name="power-menu-button",
child=Label(name="button-label", markup=icons.logout),
on_clicked=self.logout,
)
self.btn_reboot = Button(
name="powermenu-button",
child=MaterialIcon("restart_alt", 45),
name="power-menu-button",
child=Label(name="button-label", markup=icons.reboot),
on_clicked=self.reboot,
)
self.btn_shutdown = Button(
name="powermenu-button",
child=MaterialIcon("power_settings_new", 45),
name="power-menu-button",
child=Label(name="button-label", markup=icons.shutdown),
on_clicked=self.poweroff,
)
@@ -67,22 +67,23 @@ class PowerMenu(Box):
def close_menu(self):
self.launcher.close()
def lock(self, *_):
GLib.spawn_command_line_async("hyprlock --immediate")
def lock(self, *args):
exec_shell_command_async("hyprlock --immediate")
self.close_menu()
def suspend(self, *_):
GLib.spawn_command_line_async("systemctl suspend")
def suspend(self, *args):
exec_shell_command_async("systemctl suspend")
self.close_menu()
def logout(self, *_):
GLib.spawn_command_line_async("hyprctl dispatch exit")
def logout(self, *args):
exec_shell_command_async("hyprctl dispatch exit")
self.close_menu()
def reboot(self, *_):
GLib.spawn_command_line_async("systemctl reboot")
def reboot(self, *args):
exec_shell_command_async("systemctl reboot")
self.close_menu()
def poweroff(self, *_):
GLib.spawn_command_line_async("systemctl poweroff")
def poweroff(self, *args):
exec_shell_command_async("systemctl poweroff")
self.close_menu()
+4 -2
View File
@@ -6,7 +6,7 @@ from fabric.widgets.button import Button
from fabric.widgets.entry import Entry
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
from snippets import MaterialIcon
CACHE_DIR = os.getenv(
"XDG_CACHE_HOME", os.path.join(os.path.expanduser("~"), ".cache", "fabric")
@@ -37,7 +37,7 @@ class Sh(Box):
self.header_box = Box(
spacing=10,
orientation="h",
children=[self.search_entry, MaterialIcon("terminal")],
children=[self.search_entry],
)
self.launcher_box = Box(
@@ -53,9 +53,11 @@ class Sh(Box):
if not self.viewport:
self.viewport = Box(name="viewport", spacing=10, orientation="v")
self.scrolled_window = ScrolledWindow(
name="scrolled-window",
spacing=10,
h_scrollbar_policy="never",
v_scrollbar_policy="never",
# min_content_size=(-1, -1),
child=self.viewport,
)
self.launcher_box.add(self.scrolled_window)
+10 -8
View File
@@ -6,7 +6,7 @@ from fabric.widgets.button import Button
from fabric.widgets.entry import Entry
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
from snippets import MaterialIcon
import snippets.iconss as icons
class TodoManager(Box):
@@ -36,7 +36,7 @@ class TodoManager(Box):
self.todo_entry,
Button(
name="add-button",
child=MaterialIcon("add"),
child=Label(name="todo-add-button", markup=icons.add),
tooltip_text="Add Todo",
on_clicked=lambda *_: self.handle_add_todo(
self.todo_entry.get_text()
@@ -59,8 +59,9 @@ class TodoManager(Box):
if not self.viewport:
self.viewport = Box(name="viewport", spacing=4, orientation="v")
self.scrolled_window = ScrolledWindow(
name="todo-content",
name="scrolled-window",
spacing=10,
min_content_size=(-1, -1),
h_scrollbar_policy="never",
child=self.viewport,
)
@@ -82,7 +83,7 @@ class TodoListManager:
def __init__(self, launcher):
self.launcher = launcher
self.todos = []
self.todo_file = os.path.expanduser("~/.local/share/fabric/todos.json")
self.todo_file = os.path.expanduser("~/.local/share/modus/todos.json")
self.load_todos()
def load_todos(self):
@@ -130,9 +131,10 @@ class TodoListManager:
)
checkbox = Button(
name="todo-checkbox",
child=MaterialIcon(
"check_box" if todo["completed"] else "check_box_outline_blank"
name="checkbox",
child=Label(
name="todo-checkbox",
markup=icons.check_box if todo["completed"] else icons.box,
),
on_clicked=lambda _, todo_id=todo["id"]: self.toggle_todo(todo_id),
)
@@ -147,7 +149,7 @@ class TodoListManager:
delete_button = Button(
name="delete-button",
child=MaterialIcon("delete"),
child=Label(name="todo-delete-button", markup=icons.trash),
on_clicked=lambda _, todo_id=todo["id"]: self.delete_todo(todo_id),
)
+10 -12
View File
@@ -4,8 +4,8 @@ from fabric.widgets.image import Image
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.scrolledwindow import ScrolledWindow
from snippets import MaterialIcon
from services import NetworkClient
import snippets.iconss as icons
class WifiNetworkSlot(CenterBox):
@@ -15,7 +15,7 @@ class WifiNetworkSlot(CenterBox):
self.client = client
self.connecting = False # Tracks if we are connecting/disconnecting
self.connection_icon = MaterialIcon("signal_wifi_off", 12)
self.connection_icon = Label(name="wifi-connection", markup=icons.wifi_off)
self.connect_button = Button(
name="wifi-connect",
label="Connect",
@@ -68,9 +68,7 @@ class WifiNetworkSlot(CenterBox):
self.connect_button.sensitive = True
if not self.connecting:
self.connection_icon.set_label(
"signal_wifi_4_bar" if connected else "signal_wifi_off"
)
self.connection_icon.set_markup(icons.wifi if connected else icons.wifi_off)
self.connect_button.set_label("Disconnect" if connected else "Connect")
@@ -84,8 +82,8 @@ class WifiManager(Box):
)
self.client = NetworkClient()
self.scan_icon = MaterialIcon("wifi_find")
self.toggle_icon = MaterialIcon("signal_wifi_off")
self.scan_icon = Label(name="wifi-scan-icon", markup=icons.scan)
self.toggle_icon = Label(name="wifi-toggle-icon", markup=icons.wifi_off)
self.scan_button = Button(
name="wifi-scan",
@@ -113,8 +111,10 @@ class WifiManager(Box):
end_children=self.toggle_button,
),
ScrolledWindow(
name="scrolled-window",
name="wifi-available",
v_expand=True,
h_scrollbar_policy="never",
min_content_size=(-1, -1),
child=self.available_box,
),
]
@@ -135,10 +135,8 @@ class WifiManager(Box):
self.build_wifi_options()
def update_toggle_icon(self):
self.toggle_icon.set_label(
"signal_wifi_4_bar"
if self.client.wifi_device.enabled
else "signal_wifi_off"
self.toggle_icon.set_markup(
icons.wifi if self.client.wifi_device.enabled else icons.wifi_off
)
def build_wifi_options(self):
-1
View File
@@ -2,7 +2,6 @@ from fabric.widgets.centerbox import CenterBox
from fabric.widgets.stack import Stack
from fabric.widgets.wayland import WaylandWindow as Window
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from modules.launcher.components import (
AppLauncher,
BluetoothConnections,
+1 -3
View File
@@ -1,11 +1,9 @@
import time
from fabric.widgets.wayland import WaylandWindow as Window
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from gi.repository import GLib, GObject
from gi.repository import GLib
from fabric.widgets.centerbox import CenterBox
from services import brightness, audio
from snippets import Animator, MaterialIcon
def create_progress_bar(percentage, width=150):