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

-312
View File
@@ -1,312 +0,0 @@
from fabric.hyprland.widgets import Language, get_hyprland_connection
from fabric.hyprland.service import HyprlandEvent
from fabric.system_tray.widgets import SystemTray
from fabric.widgets.shapes import Corner
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.datetime import DateTime
from fabric.widgets.label import Label
from fabric.widgets.wayland import WaylandWindow as Window
from modules.bar.components import Metrics, workspace, SystemIndicators, UpdatesWidget
import config.data as data
from gi.repository import Gdk, GLib
import utils.icons as icons
import os
import json
class Bar(Window):
def __init__(self, **kwargs):
super().__init__(
name="bar",
layer="top",
anchor="left top right"
if not data.VERTICAL and not data.BOTTOM_BAR
else "top right bottom"
if data.VERTICAL and data.VERTICAL_RIGHT_ALIGN
else "top left bottom"
if data.VERTICAL
else "bottom left right",
exclusivity="auto",
visible=True,
all_visible=True,
)
self.launcher = kwargs.get("launcher", None)
self.component_visibility = data.BAR_COMPONENTS_VISIBILITY
# Determine if we should use vertical layout for components
is_vertical_layout = data.VERTICAL
self.workspaces = Button(
child=workspace,
name="workspaces",
)
self.connection = get_hyprland_connection()
self.tray = SystemTray(
name="tray",
spacing=4,
icon_size=16,
orientation="h" if not is_vertical_layout else "v",
)
self.date_time = Button(
name="datetime",
child=DateTime(
formatters=["%-I:%M:%p 󰧞 %a %d %b"]
if not is_vertical_layout
else ["%I\n%M\n%p"],
h_align="center" if not is_vertical_layout else "fill",
v_align="center",
),
on_clicked=lambda *_: self.calendar(),
)
self.date_time.connect("enter_notify_event", self.on_button_enter)
self.date_time.connect("leave_notify_event", self.on_button_leave)
self.button_app = Button(
name="button-bar",
child=Label(name="button-bar-label", markup=icons.apps),
on_clicked=lambda *_: self.search_apps(),
)
self.button_app.connect("enter_notify_event", self.on_button_enter)
self.button_app.connect("leave_notify_event", self.on_button_leave)
self.updates = UpdatesWidget()
self.updates.connect("enter_notify_event", self.on_button_enter)
self.updates.connect("leave_notify_event", self.on_button_leave)
self.lang_label = Label(name="lang-label")
self.language = Button(
name="language", h_align="center", v_align="center", child=self.lang_label
)
self.on_language_switch()
self.connection.connect("event::activelayout", self.on_language_switch)
self.metrics = Metrics()
self.indicators = SystemIndicators(launcher=self.launcher)
self.applets = Box(
name="system-indicator",
spacing=4,
orientation="h" if not is_vertical_layout else "v",
children=[self.language, self.indicators],
)
# Apply visibility settings to components
self.apply_component_visibility()
self.h_start_children = [self.button_app, self.updates]
self.h_center_children = [
self.date_time,
self.workspaces,
self.metrics,
self.applets,
]
self.h_end_children = [self.tray]
self.v_start_children = [
self.button_app,
self.updates,
self.workspaces,
self.metrics,
]
self.v_end_children = [self.tray, self.applets, self.date_time]
self.v_all_children = []
self.v_all_children.extend(self.v_start_children)
self.v_all_children.extend(self.v_end_children)
# Use centered layout when both vertical and centered_bar are enabled
is_centered_bar = is_vertical_layout and getattr(data, "CENTERED_BAR", False)
self.bar = CenterBox(
name="bar",
orientation="h" if not is_vertical_layout else "v",
h_align="fill",
v_align="fill",
start_children=None
if is_centered_bar
else Box(
name="start-container",
spacing=4,
orientation="h" if not is_vertical_layout else "v",
children=self.h_start_children
if not is_vertical_layout
else self.v_start_children,
),
center_children=Box(
orientation="v",
spacing=4,
children=self.v_all_children
if is_centered_bar
else Box(
name="center-container",
spacing=4,
orientation="h" if not is_vertical_layout else "v",
children=self.h_center_children if not is_vertical_layout else None,
),
)
if is_vertical_layout
else Box(
name="center-container",
spacing=4,
orientation="h" if not is_vertical_layout else "v",
children=self.h_center_children if not is_vertical_layout else None,
),
end_children=None
if is_centered_bar
else Box(
name="end-container",
spacing=4,
orientation="h" if not is_vertical_layout else "v",
children=self.h_end_children
if not is_vertical_layout
else self.v_end_children,
),
)
self.children = self.bar
def apply_component_visibility(self):
"""Apply saved visibility settings to all components"""
components = {
"metrics": self.metrics,
"indicators": self.applets,
"updates": self.updates,
"workspaces": self.workspaces,
"button_app": self.button_app,
# "button_tools": self.button_tools,
"language": self.language,
"date_time": self.date_time,
"tray": self.tray,
}
for component_name, widget in components.items():
if component_name in self.component_visibility:
widget.set_visible(self.component_visibility[component_name])
def toggle_component_visibility(self, component_name):
"""Toggle visibility for a specific component"""
components = {
"metrics": self.metrics,
"indicators": self.applets,
"updates": self.updates,
"workspaces": self.workspaces,
"button_app": self.button_app,
# "button_tools": self.button_tools,
"language": self.language,
"date_time": self.date_time,
"tray": self.tray,
}
if component_name in components and component_name in self.component_visibility:
# Toggle the visibility state
self.component_visibility[component_name] = not self.component_visibility[
component_name
]
# Apply the new state
components[component_name].set_visible(
self.component_visibility[component_name]
)
# Update the configuration
config_file = os.path.expanduser(
f"~/{data.APP_NAME}/config/assets/config.json"
)
try:
if os.path.exists(config_file):
with open(config_file, "r") as f:
config = json.load(f)
# Update the config with the new visibility state
config[f"bar_{component_name}_visible"] = self.component_visibility[
component_name
]
# Write the updated config back to file
with open(config_file, "w") as f:
json.dump(config, f, indent=4)
except (json.JSONDecodeError, IOError) as e:
print(f"Error updating config file: {e}")
# Revert the visibility change if config update fails
self.component_visibility[
component_name
] = not self.component_visibility[component_name]
components[component_name].set_visible(
self.component_visibility[component_name]
)
return self.component_visibility[component_name]
return None
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 search_apps(self):
self.launcher.open("launcher")
def calendar(self):
self.launcher.open("calendar")
def on_language_switch(self, _=None, event: HyprlandEvent = None):
lang = event.data[1] if event else Language().get_label()
self.language.set_tooltip_text(lang)
if not data.VERTICAL:
self.lang_label.set_label(lang[:2].lower())
else:
self.lang_label.add_style_class("icon")
self.lang_label.set_markup(icons.keyboard)
class MyCorner(Box):
def __init__(self, position):
super().__init__(
name="corner-container",
children=Corner(
name="corner",
orientation=position,
size=20,
),
)
class Corners(Window):
def __init__(self):
super().__init__(
layer="top",
anchor="top left bottom right",
pass_through=True,
child=Box(
orientation="vertical",
children=[
Box(
children=[
MyCorner("top-left"),
Box(h_expand=True),
MyCorner("top-right"),
]
),
Box(v_expand=True),
Box(
children=[
MyCorner("bottom-left"),
Box(h_expand=True),
MyCorner("bottom-right"),
]
),
],
),
)
-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)