chore(starting from new):

This commit is contained in:
sankalp committed 2025-05-11 12:12:46 +05:45
1 parent e5dc91caa3
commit b0b8c19562
82 files changed
-29339

No files matched your search

-4
View File
@@ -1,4 +0,0 @@
from .metrics import Metrics
from .workspace import workspace
from .system_indicators import SystemIndicators
from .updates import UpdatesWidget
-406
View File
@@ -1,406 +0,0 @@
import subprocess
import json
import psutil
from gi.repository import GLib, Gdk
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from fabric.widgets.circularprogressbar import CircularProgressBar
from fabric.widgets.revealer import Revealer
from fabric.core.fabricator import Fabricator
from fabric.widgets.button import Button
import utils.icons as icons
import config.data as data
class MetricsProvider:
def __init__(self):
self.cpu = 0.0
self.mem = 0.0
self.swap = 0.0
self.disk = []
self.gpu = []
self.bat_percent = 0.0
self.bat_charging = None
GLib.timeout_add_seconds(1, self._update)
def _update(self):
# Get non-blocking usage percentages (interval=0)
# The first call may return 0, but subsequent calls will provide consistent values.
self.cpu = psutil.cpu_percent(interval=0)
mem = psutil.virtual_memory()
self.mem = mem.percent
swap = psutil.swap_memory()
self.swap = swap.percent
self.disk = [psutil.disk_usage(path).percent for path in data.BAR_METRICS_DISKS]
info = self.get_gpu_info()
self.gpu = [int(v["gpu_util"][:-1]) for v in info]
battery = psutil.sensors_battery()
if battery is None:
self.bat_percent = 0.0
self.bat_charging = None
else:
self.bat_percent = battery.percent
self.bat_charging = battery.power_plugged
return True
def get_metrics(self):
return (self.cpu, self.mem, self.swap, self.disk)
def get_battery(self):
return (self.bat_percent, self.bat_charging)
def get_gpu_info(self):
try:
return json.loads(subprocess.check_output(["nvtop", "-s"]))
except:
return []
shared_provider = MetricsProvider()
class SingularMetric:
def __init__(self, id, name, icon):
self.is_vertical_layout = data.VERTICAL
self.name_markup = name
self.icon_markup = icon
self.icon = Label(name="metrics-icon", markup=icon)
self.circle = CircularProgressBar(
name="metrics-circle",
value=0,
size=26,
line_width=2,
start_angle=150,
end_angle=390,
style_classes=id,
child=self.icon,
)
self.circle_button = Button(
name=f"metrics-{id}-button",
child=self.circle,
style="padding: 0; margin: 0; background: none;",
)
self.circle_button.add_events(
Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK
)
self.circle_button.connect("enter-notify-event", self.on_button_enter)
self.circle_button.connect("leave-notify-event", self.on_button_leave)
self.circle_button.connect("clicked", self.on_circle_clicked)
self.level = Label(
name="metrics-level",
style_classes=id,
label="0%",
style="margin: 4px 0px;" if self.is_vertical_layout else "margin: 0 4px;",
)
self.revealer = Revealer(
name=f"metrics-{id}-revealer",
transition_duration=250,
transition_type="slide-up" if self.is_vertical_layout else "slide-left",
child=self.level,
child_revealed=False,
)
self.box = Box(
name=f"metrics-{id}-box",
orientation="v" if self.is_vertical_layout else "h",
spacing=0,
children=[self.circle_button, self.revealer],
)
# Connect events for cursor changes to the box as well
self.box.connect("enter-notify-event", self.on_button_enter)
self.box.connect("leave-notify-event", self.on_button_leave)
# Set initial tooltip - moved after box is created
self.update_tooltip("0%")
def update_tooltip(self, value_text):
tooltip = f"{self.icon_markup} {self.name_markup}: {value_text}"
# self.box.set_tooltip_markup(tooltip)
self.circle_button.set_tooltip_markup(tooltip)
self.circle.set_tooltip_markup(tooltip)
self.icon.set_tooltip_markup(tooltip)
def markup(self):
"""Get formatted text for tooltip display"""
return f"{self.icon_markup} {self.name_markup}: {self.level.get_label()}"
def on_button_enter(self, widget, event):
window = widget.get_window()
if window:
window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
def on_button_leave(self, widget, event):
window = widget.get_window()
if window:
window.set_cursor(None)
def on_circle_clicked(self, button):
# Toggle the revealed state of the label
current_state = self.revealer.get_child_revealed()
self.revealer.set_reveal_child(not current_state)
class Metrics(Box):
def __init__(self, **kwargs):
# Determine if we should use vertical layout for components
self.is_vertical_layout = data.VERTICAL
super().__init__(
name="metrics",
spacing=0,
orientation="h" if not self.is_vertical_layout else "v",
visible=True,
all_visible=True,
style="padding:4px 0px" if self.is_vertical_layout else "padding: 0px 4px;",
**kwargs,
)
self.batt_fabricator = Fabricator(
poll_from=lambda v: shared_provider.get_battery(),
on_changed=lambda f, v: self.update_battery,
interval=1000,
stream=False,
default_value=0,
)
self.batt_fabricator.changed.connect(self.update_battery)
GLib.idle_add(self.update_battery, None, shared_provider.get_battery())
visible = getattr(
data,
"METRICS_VISIBLE",
{"cpu": True, "ram": True, "disk": True, "gpu": False, "swap": True},
)
disks = (
[
SingularMetric(
"disk",
f"DISK ({path})" if len(data.BAR_METRICS_DISKS) != 1 else "DISK",
icons.disk,
)
for path in data.BAR_METRICS_DISKS
]
if visible.get("disk", True)
else []
)
gpu_info = shared_provider.get_gpu_info()
gpus = (
[
SingularMetric(
f"gpu",
f"GPU ({v['device_name']})" if len(gpu_info) != 1 else "GPU",
icons.gpu,
)
for v in gpu_info
]
if visible.get("gpu", True)
else []
)
self.cpu = (
SingularMetric("cpu", "CPU", icons.cpu)
if visible.get("cpu", True)
else None
)
self.ram = (
SingularMetric("ram", "RAM", icons.memory)
if visible.get("ram", True)
else None
)
self.swap = (
SingularMetric("swap", "SWAP", icons.swap)
if visible.get("swap", True)
else None
)
self.bat = (
SingularMetric("bat", "BATTERY", icons.battery)
if visible.get("bat", True)
else None
)
self.disk = disks
self.gpu = gpus
# Add only enabled metrics
for disk in self.disk:
self.add(disk.box)
self.add(Box(name="metrics-sep"))
if self.ram:
self.add(self.ram.box)
self.add(Box(name="metrics-sep"))
if self.cpu:
self.add(self.cpu.box)
for gpu in self.gpu:
self.add(Box(name="metrics-sep"))
self.add(gpu.box)
if self.swap:
self.add(Box(name="metrics-sep"))
self.add(self.swap.box)
if self.bat:
self.add(Box(name="metrics-sep"))
self.add(self.bat.box)
# Connect events directly to the button
self.connect("enter-notify-event", self.on_mouse_enter)
self.connect("leave-notify-event", self.on_mouse_leave)
GLib.timeout_add_seconds(1, self.update_metrics)
self.hide_timer = None
self.hover_counter = 0
def _format_percentage(self, value: int) -> str:
return f"{value}%"
def on_mouse_enter(self, widget, event):
window = widget.get_window()
if window:
window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
if not data.VERTICAL:
self.hover_counter += 1
if self.hide_timer is not None:
GLib.source_remove(self.hide_timer)
self.hide_timer = None
if self.cpu:
self.cpu.revealer.set_reveal_child(True)
if self.ram:
self.ram.revealer.set_reveal_child(True)
for disk in self.disk:
disk.revealer.set_reveal_child(True)
for gpu in self.gpu:
gpu.revealer.set_reveal_child(True)
if self.swap:
self.swap.revealer.set_reveal_child(True)
if self.bat:
self.bat.revealer.set_reveal_child(True)
return False
def on_mouse_leave(self, widget, event):
window = widget.get_window()
if window:
window.set_cursor(None)
if not data.VERTICAL:
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):
if not data.VERTICAL:
if self.cpu:
self.cpu.revealer.set_reveal_child(False)
if self.ram:
self.ram.revealer.set_reveal_child(False)
for disk in self.disk:
disk.revealer.set_reveal_child(False)
for gpu in self.gpu:
gpu.revealer.set_reveal_child(False)
self.hide_timer = None
return False
def update_metrics(self):
cpu, mem, swap, disk = shared_provider.get_metrics()
gpu = shared_provider.gpu
if self.cpu:
self.cpu.circle.set_value(cpu / 100.0)
value_text = self._format_percentage(int(cpu))
self.cpu.level.set_label(value_text)
self.cpu.update_tooltip(value_text)
if self.ram:
self.ram.circle.set_value(mem / 100.0)
value_text = self._format_percentage(int(mem))
self.ram.level.set_label(value_text)
self.ram.update_tooltip(value_text)
for i, disk_metric in enumerate(self.disk):
if i < len(disk):
value_text = self._format_percentage(int(disk[i]))
disk_metric.circle.set_value(disk[i] / 100.0)
disk_metric.level.set_label(value_text)
disk_metric.update_tooltip(value_text)
for i, gpu_metric in enumerate(self.gpu):
if i < len(gpu):
value_text = self._format_percentage(int(gpu[i]))
gpu_metric.circle.set_value(gpu[i] / 100.0)
gpu_metric.level.set_label(value_text)
gpu_metric.update_tooltip(value_text)
if self.swap:
self.swap.circle.set_value(swap / 100.0)
value_text = self._format_percentage(int(swap))
self.swap.level.set_label(value_text)
self.swap.update_tooltip(value_text)
if self.bat:
bat_percent, _ = shared_provider.get_battery()
self.bat.circle.set_value(bat_percent / 100.0)
value_text = self._format_percentage(int(bat_percent))
self.bat.level.set_label(value_text)
self.bat.update_tooltip(value_text)
# Tooltip: only show enabled metrics
tooltip_metrics = []
if self.disk: tooltip_metrics.extend(self.disk)
if self.swap: tooltip_metrics.append(self.swap)
if self.bat: tooltip_metrics.append(self.bat)
if self.ram: tooltip_metrics.append(self.ram)
if self.cpu: tooltip_metrics.append(self.cpu)
if self.gpu: tooltip_metrics.extend(self.gpu)
self.set_tooltip_markup((" - " if not data.VERTICAL else "\n").join([v.markup() for v in tooltip_metrics]))
return True
def update_battery(self, sender, battery_data):
if self.bat is None:
return
value, charging = battery_data
if value == 0:
if self.bat:
self.bat.box.set_visible(False)
else:
if self.bat:
self.bat.box.set_visible(True)
self.bat.circle.set_value(value / 100)
percentage = int(value)
self.bat.level.set_label(self._format_percentage(percentage))
# Apply alert styling if battery is low AND not charging
if percentage <= 15 and charging == False:
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")
# Choose the icon based on charging state first, then battery level
if percentage == 100:
self.bat.icon.set_markup(icons.battery)
charging_status = f"{icons.bat_full} Fully Charged"
elif charging == True:
self.bat.icon.set_markup(icons.charging)
charging_status = f"{icons.bat_charging} Charging"
elif percentage <= 15 and charging == False:
self.bat.icon.set_markup(icons.alert)
charging_status = f"{icons.bat_low} Low Battery"
elif charging == False:
self.bat.icon.set_markup(icons.discharging)
charging_status = f"{icons.bat_discharging} Discharging"
else:
self.bat.icon.set_markup(icons.battery)
charging_status = "Battery"
# Update individual tooltip for battery
self.bat.update_tooltip(f"{charging_status}: {self._format_percentage(percentage)}")
# Update the combined tooltip
self.update_metrics()
-258
View File
@@ -1,258 +0,0 @@
import subprocess
from fabric import Fabricator
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.bluetooth import BluetoothClient
from services import network_client, audio, notification_service
import utils.icons as icons
import config.data as data
class SystemIndicators(Box):
def __init__(self, **kwargs):
# Determine if we should use vertical layout for components
is_vertical_layout = data.VERTICAL
super().__init__(
orientation="h" if not is_vertical_layout else "v", spacing=0, **kwargs
)
self.launcher = kwargs.get("launcher", None)
self.bluetooth_icon = Label(name="system-indicator-icon")
self.wifi_icon = Label(name="system-indicator-icon")
self.volume_icon_button = Label(name="system-indicator-icon")
self.microphone_icon = Label(name="system-indicator-icon")
self.idle_label = Label(name="system-indicator-icon")
self.night_label = Label(name="system-indicator-icon")
self.power_label = Label(name="system-indicator-icon")
self.notification_icon = Button(name="system-indicator-icon")
self.notification_label = Label(name="system-notif-label")
self.notification_icon.add(self.notification_label)
self.notification_icon.connect("clicked", self.open_notif_center)
for widget in [
self.bluetooth_icon,
self.wifi_icon,
self.volume_icon_button,
self.microphone_icon,
self.idle_label,
self.night_label,
self.power_label,
self.notification_icon,
]:
self.add(widget)
self.audio_service = audio
self.bluetooth_client = BluetoothClient()
self.network_client = network_client
self.notification_service = notification_service
# 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)
self.notification_service.connect("dnd", self.update_notification_status)
self.notification_service.connect(
"notification_count", self.update_notification_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()
self.update_notification_status()
def update_all_statuses(self, *_args):
self.update_idle_night_status()
self.update_power_profile()
self.update_network_status()
def update_idle_night_status(self, *_):
# Update idle status
has_idle = (
subprocess.run(["pgrep", "-x", "wlinhibit"], capture_output=True).returncode
== 0
)
self.idle_label.set_visible(has_idle)
self.idle_label.set_markup(icons.coffee if has_idle else "")
# Update night mode status
has_night = (
subprocess.run(
["pgrep", "-x", "hyprsunset"], capture_output=True
).returncode
== 0
)
self.night_label.set_visible(has_night)
self.night_label.set_markup(icons.night if has_night else "")
def update_volume_status(self, *_):
stream = self.audio_service.speaker
if not stream:
self.volume_icon_button.set_visible(False)
return
self.volume_icon_button.set_visible(True)
# Normalize volume to integer percentage (0-100)
volume_level = stream.volume
if volume_level > 1:
volume_level = min(int(volume_level), 100)
else:
volume_level = int(volume_level * 100)
is_muted = stream.muted
if is_muted:
icon = icons.vol_off
elif volume_level > 74:
icon = icons.vol_high
elif volume_level > 0:
icon = icons.vol_medium
else:
icon = icons.vol_mute
self.volume_icon_button.set_markup(icon)
tooltip_text = "Muted" if is_muted else f"Volume: {volume_level}%"
self.volume_icon_button.set_tooltip_text(tooltip_text)
def update_mic_status(self, *_):
mic = self.audio_service.microphone
if not mic:
self.microphone_icon.set_visible(False)
return
self.microphone_icon.set_visible(True)
# Normalize volume to integer percentage (0-100)
volume_level = mic.volume
if volume_level > 1:
volume_level = min(int(volume_level), 100)
else:
volume_level = int(volume_level * 100)
is_muted = mic.muted
icon = icons.mic_muted if is_muted else icons.mic
self.microphone_icon.set_markup(icon)
tooltip_text = "Muted" if is_muted else f"Microphone: {volume_level}%"
self.microphone_icon.set_tooltip_text(tooltip_text)
def update_bluetooth_status(self, *_):
if self.bluetooth_client.enabled:
self.bluetooth_icon.set_visible(True)
# Get a list of connected devices
connected_devices = [
device.name
for device in self.bluetooth_client.devices
if device.connected
]
if connected_devices:
self.bluetooth_icon.set_markup(icons.bluetooth_connected)
self.bluetooth_icon.set_tooltip_text(f"{', '.join(connected_devices)}")
else:
self.bluetooth_icon.set_markup(icons.bluetooth)
self.bluetooth_icon.set_tooltip_text(
"Bluetooth is enabled, no devices connected"
)
else:
self.bluetooth_icon.set_visible(True) # Keep it visible
self.bluetooth_icon.set_markup(icons.bluetooth_off)
self.bluetooth_icon.set_tooltip_text("Bluetooth is disabled")
def update_network_status(self, *_):
primary_device = self.network_client.primary_device
self.wifi_icon.set_visible(True)
if primary_device == "wifi" and self.network_client.wifi_device:
wifi_device = self.network_client.wifi_device
self.wifi_icon.set_tooltip_text(
wifi_device.ssid if wifi_device.ssid else "No WiFi Connection"
)
if wifi_device.enabled:
strength = wifi_device.strength
if strength > 0:
if strength < 25:
icon_label = icons.wifi_0
elif strength < 50:
icon_label = icons.wifi_1
elif strength < 75:
icon_label = icons.wifi_2
else:
icon_label = icons.wifi_3
else:
icon_label = icons.wifi_off
else:
icon_label = icons.wifi_off
elif primary_device == "wired":
icon_label = icons.lan
self.wifi_icon.set_tooltip_text("Wired Connection")
else:
icon_label = icons.wifi_off # No network
self.wifi_icon.set_markup(icon_label)
def update_power_profile(self, *_):
try:
result = subprocess.run(
["powerprofilesctl", "get"], capture_output=True, text=True
)
current_profile = result.stdout.strip()
profile_icons = {
"performance": icons.power_performance,
"balanced": "",
"power-saver": icons.power_saving,
}
icon = profile_icons.get(current_profile)
tooltip = (
f"Power Profile: {current_profile.capitalize()}"
if current_profile
else "Unknown Profile"
)
self.power_label.set_visible(bool(icon)) # Only show if there's an icon
self.power_label.set_markup(icon)
self.power_label.set_tooltip_text(tooltip)
except Exception as e:
self.power_label.set_visible(False)
self.power_label.set_tooltip_text(f"Error fetching power profile: {e}")
def update_notification_status(self, *_):
"""Update the notification icon based on DND state and notification count."""
count = self.notification_service.count
if count == 0 and not self.notification_service.dont_disturb:
self.notification_icon.set_visible(False)
return
if self.notification_service.dont_disturb:
self.notification_label.set_markup(icons.notifications_off)
self.notification_icon.set_tooltip_text("Do Not Disturb: On")
self.notification_icon.set_visible(True)
else:
# At this point count must be > 0
self.notification_label.set_markup(icons.notifications_clear)
self.notification_icon.set_tooltip_text(f"Notifications: {count}")
self.notification_icon.set_visible(True)
def open_notif_center(self, *_):
self.launcher.open("notification-center")
-115
View File
@@ -1,115 +0,0 @@
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.button import Button
from fabric.widgets.stack import Stack
from gi.repository import Gtk
import utils.icons as icons
import config.data as data
class UpdatesWidget(Button):
"""A widget to display the number of available updates."""
def __init__(
self,
**kwargs,
):
# Determine if we should use vertical layout for components
is_vertical_layout = data.VERTICAL
# Initialize the button with specific name and style
super().__init__(
name="update",
orientation="h" if not is_vertical_layout else "v",
**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)
# Create boxes with proper orientation
self.update_box = CenterBox(
orientation="v" if is_vertical_layout else "h",
center_children=[self.update_icon, self.update_level_label],
)
self.updated_box = CenterBox(
orientation="v" if is_vertical_layout else "h",
center_children=[self.updated_icon],
)
# Stack to switch between states
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(
orientation="v" if is_vertical_layout else "h",
children=self.stack,
)
# Configure stack transitions
self.stack.set_homogeneous(False)
self.stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
# Connect click handler
self.connect("button-press-event", self.on_button_press)
# Check for updates every minute
invoke_repeater(60000, self.update, initial_call=True)
def update_values(self, value: str) -> bool:
"""Update the widget state based on update check results"""
try:
value = json.loads(value)
except json.JSONDecodeError:
return False
update_count = str(value["total"])
self.update_level_label.set_label(update_count)
# Show appropriate state based on 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) -> bool:
"""Handle button press events"""
if event.button == 1: # Left click
# Run update command
exec_shell_command_async(
f"{self.script_file} -arch -up",
lambda _: None,
)
self.update()
return True
else:
# Other clicks just refresh
self.update()
def update(self) -> bool:
"""Check for system updates"""
exec_shell_command_async(
f"{self.script_file} -arch",
lambda output: self.update_values(output),
)
return True
-30
View File
@@ -1,30 +0,0 @@
import json
from fabric.hyprland.service import Hyprland
from fabric.widgets.label import Label
import config.data as data
connection = Hyprland()
workspaceData = connection.send_command("j/activeworkspace").reply
activeWorkspace = json.loads(workspaceData.decode("utf-8"))["name"]
# Create the workspace label with proper initialization
if not data.VERTICAL:
workspace = Label(label=f"Workspace {activeWorkspace}", name="workspace-name")
else:
workspace = Label(
v_align="center",
label="W\no\nr\nk\ns\np\na\nc\ne\n\n" + activeWorkspace,
name="workspace-name",
)
def on_workspace(obj, signal):
global activeWorkspace
activeWorkspace = json.loads(signal.data[0])
if not data.VERTICAL:
workspace.set_label(f"Workspace {activeWorkspace}")
else:
workspace.set_label(f"W\no\nr\nk\ns\np\na\nc\ne\n\n{activeWorkspace}")
connection.connect("event::workspace", on_workspace)