Huge updates: ControlCenter, DropDown Menu

This commit is contained in:
sankalp committed 2025-07-31 20:46:02 +05:45
1 parent 1f2d0a33f4
commit 9d83d960ef
34 files changed
+3261 -1440

No files matched your search

+257 -252
View File
@@ -1,137 +1,34 @@
from gi.repository import GLib
import utils.icons as icons
from fabric.bluetooth import BluetoothClient
from fabric.utils import get_relative_path
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.svg import Svg
from services.battery import Battery as BatteryService
from services.network import NetworkClient
from services.network import NetworkService
from services.battery import Battery
from utils.roam import modus_service
class WifiIndicator(Button):
class BluetoothIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.network = NetworkClient()
self.wifi_icon = Svg(
name="indicators-icon",
size=24,
svg_file=get_relative_path("../../../config/assets/icons/wifi.svg"),
)
self.add(self.wifi_icon)
self.network.connect("ready", self.on_network_ready)
if self.network.is_ready:
self.setup_signals()
def on_network_ready(self, *args):
self.setup_signals()
def setup_signals(self):
self.network.connect("changed", self.on_network_changed)
self.network.connect("wifi-device-added", self.on_wifi_device_added)
self.network.connect("wifi-device-removed", self.on_wifi_device_removed)
self.network.connect("ethernet-device-added", self.on_ethernet_device_added)
self.network.connect("ethernet-device-removed", self.on_ethernet_device_removed)
if self.network.wifi_device:
self.network.wifi_device.connect("changed", self.on_wifi_changed)
self.network.wifi_device.connect("ap-added", self.on_wifi_changed)
self.network.wifi_device.connect("ap-removed", self.on_wifi_changed)
if self.network.ethernet_device:
self.network.ethernet_device.connect("changed", self.on_ethernet_changed)
self.update_state()
def update_state(self):
if (
self.network.ethernet_device
and self.network.ethernet_device.state == "activated"
):
self.wifi_icon.set_from_file(
get_relative_path("../../../config/assets/icons/wifi.svg")
)
tooltip = f"Connected to Ethernet ({self.network.ethernet_device.speed})"
self.set_tooltip_text(tooltip)
return
if not self.network.wifi_device:
self.wifi_icon.set_from_file(
get_relative_path("../../../config/assets/icons/wifi-off.svg")
)
tooltip = "No WiFi device found"
elif not self.network.wifi_device.wireless_enabled:
self.wifi_icon.set_from_file(
get_relative_path("../../../config/assets/icons/wifi-off.svg")
)
tooltip = "WiFi disabled"
else:
active_ap = self.network.wifi_device.active_access_point
if active_ap:
strength = active_ap.strength
# Use wifi.svg for all connected states since we don't have signal strength icons
self.wifi_icon.set_from_file(
get_relative_path("../../../config/assets/icons/wifi.svg")
)
tooltip = f"Connected to {active_ap.ssid} ({strength}%)"
else:
self.wifi_icon.set_from_file(
get_relative_path("../../../config/assets/icons/wifi-off.svg")
)
tooltip = "Not connected"
self.set_tooltip_text(tooltip)
def on_wifi_device_added(self, *args):
self.network.wifi_device.connect("changed", self.on_wifi_changed)
self.network.wifi_device.connect("ap-added", self.on_wifi_changed)
self.network.wifi_device.connect("ap-removed", self.on_wifi_changed)
self.update_state()
def on_wifi_device_removed(self, *args):
self.update_state()
def on_network_changed(self, *args):
self.update_state()
def on_wifi_changed(self, *args):
self.update_state()
def on_ethernet_device_added(self, *args):
if self.network.ethernet_device:
self.network.ethernet_device.connect("changed", self.on_ethernet_changed)
self.update_state()
def on_ethernet_device_removed(self, *args):
self.update_state()
def on_ethernet_changed(self, *args):
self.update_state()
class BluetoothIndicator(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
super().__init__(name="bluetooth-indicator", orientation="h", **kwargs)
self.bluetooth = BluetoothClient()
# Use SVG icons instead of font icons
self.bt_icon = Svg(
name="bt-icon",
size=20,
svg_file=get_relative_path("../../../config/assets/icons/bluetooth.svg"),
)
self.add(self.bt_icon)
self.bluetooth.connect("changed", self.on_bluetooth_changed)
self.bt_button = Button(name="bt-button", child=self.bt_icon)
self.add(self.bt_button)
modus_service.connect("bluetooth-changed", self.on_bluetooth_changed)
self.bluetooth.connect("changed", self.on_bluetooth_direct_changed)
self.bluetooth.connect("device-added", self.on_device_added)
self.bluetooth.connect("device-removed", self.on_device_removed)
self.update_modus_service_bluetooth_state()
self.update_state()
def update_state(self):
@@ -159,161 +56,269 @@ class BluetoothIndicator(Button):
)
tooltip = "No devices connected"
self.set_tooltip_text(tooltip)
self.bt_button.set_tooltip_text(tooltip)
def on_bluetooth_changed(self, *args):
def on_bluetooth_changed(self, service, new_bluetooth_state):
self.update_state()
def on_bluetooth_direct_changed(self, *args):
self.update_modus_service_bluetooth_state()
self.update_state()
def on_device_added(self, _, address):
self.update_modus_service_bluetooth_state()
self.update_state()
def on_device_removed(self, _, address):
self.update_modus_service_bluetooth_state()
self.update_state()
class Battery(Box):
def __init__(self, **kwargs):
super().__init__(orientation="v", visible=True, **kwargs)
self._battery = BatteryService()
self._battery.changed.connect(self.update_battery)
self.icon = Svg(
name="indicators-icon",
size=24,
svg_file=get_relative_path(
"../../../config/assets/icons/battery/battery-100.svg"
),
)
self.battery_button = Button(
child=self.icon,
)
self.battery_box = Box(
name="battery-box",
orientation="h",
children=[self.battery_button],
)
self.battery_button.set_has_tooltip(True)
self.battery_box.set_has_tooltip(True)
self.battery_button.connect("query-tooltip", self.on_query_tooltip)
self.battery_box.connect("query-tooltip", self.on_query_tooltip)
self.add(self.battery_box)
GLib.idle_add(self.update_battery)
def on_query_tooltip(self, *_):
tooltip = _[-1]
tooltip.set_markup(self.get_tooltip_text())
return True
def get_tooltip_text(self):
if not self._battery.is_present:
return "No battery detected"
percentage = self._battery.percentage
state = self._battery.state
capacity = int(float(self._battery.capacity.rstrip("%")))
tooltip_points = []
if state == "FULLY_CHARGED":
status = f"{icons.battery_full} Fully Charged"
elif state == "CHARGING":
status = f"{icons.bat_charging} Charging"
elif percentage <= 15 and state == "DISCHARGING":
status = f"{icons.bat_alert} Low Battery"
elif state == "DISCHARGING":
status = f"{icons.battery_0} Discharging"
def update_modus_service_bluetooth_state(self):
if not self.bluetooth.enabled:
bluetooth_state = "disabled"
else:
status = "Battery"
tooltip_points.append(f"• Status: {status}")
tooltip_points.append(f"• Level: {int(percentage)}%")
tooltip_points.append(f"• Battery Health: {capacity}%")
power_profile = self._battery.power_profile
if power_profile:
if power_profile == "power-saver":
profile_icon = icons.power_saving
elif power_profile == "performance":
profile_icon = icons.power_performance
connected_devices = self.bluetooth.connected_devices
if connected_devices:
if len(connected_devices) == 1:
device = connected_devices[0]
bluetooth_state = f"connected:{device.alias}"
if (
hasattr(device, "battery_percentage")
and device.battery_percentage > 0
):
bluetooth_state += f":{device.battery_percentage:.0f}%"
else:
bluetooth_state = f"connected:{len(connected_devices)}_devices"
else:
profile_icon = icons.power_balanced
tooltip_points.append(f"• Profile: {profile_icon} {power_profile}")
bluetooth_state = "enabled"
temp = self._battery.temperature
if temp != "N/A":
tooltip_points.append(f"• Temperature: {temp}")
modus_service.bluetooth = bluetooth_state
class NetworkIndicator(Box):
def __init__(self, **kwargs):
super().__init__(name="network-indicator", orientation="h", **kwargs)
self.network_service = NetworkService()
self.network_icon = Svg(
name="network-icon",
size=25,
svg_file=get_relative_path("../../../config/assets/icons/wifi.svg"),
)
self.network_button = Button(name="network-button", child=self.network_icon)
self.add(self.network_button)
modus_service.connect("wlan-changed", self.on_wlan_changed)
self.network_service.connect("device-ready", self.on_device_ready)
self.update_modus_service_wlan_state()
self.update_state()
def on_wlan_changed(self, service, new_wlan_state):
self.update_state()
def on_device_ready(self, *args):
if self.network_service.wifi_device:
self.network_service.wifi_device.connect(
"changed", self.on_network_direct_changed
)
if self.network_service.ethernet_device:
self.network_service.ethernet_device.connect(
"changed", self.on_network_direct_changed
)
self.update_modus_service_wlan_state()
self.update_state()
def on_network_direct_changed(self, *args):
self.update_modus_service_wlan_state()
self.update_state()
def on_network_changed(self, *args):
self.update_modus_service_wlan_state()
self.update_state()
def update_modus_service_wlan_state(self):
primary_device = self.network_service.primary_device
wlan_state = "disconnected"
if primary_device == "wifi" and self.network_service.wifi_device:
wifi = self.network_service.wifi_device
if not wifi.enabled:
wlan_state = "disabled"
elif wifi.internet == "activated":
wlan_state = f"connected:{wifi.ssid}"
if wifi.strength >= 0:
wlan_state += f":{wifi.strength}%"
elif wifi.internet == "activating":
wlan_state = f"connecting:{wifi.ssid}"
else:
wlan_state = "enabled"
elif primary_device == "wired" and self.network_service.ethernet_device:
ethernet = self.network_service.ethernet_device
if ethernet.internet == "activated":
wlan_state = "ethernet:connected"
if hasattr(ethernet, "speed") and ethernet.speed > 0:
wlan_state += f":{ethernet.speed}Mbps"
elif ethernet.internet == "activating":
wlan_state = "ethernet:connecting"
else:
wlan_state = "ethernet:disconnected"
modus_service.wlan = wlan_state
def update_state(self):
primary_device = self.network_service.primary_device
tooltip = "No network connection"
icon_file = "wifi-off.svg"
if primary_device == "wifi" and self.network_service.wifi_device:
wifi = self.network_service.wifi_device
if not wifi.enabled:
icon_file = "wifi-off.svg"
tooltip = "WiFi disabled"
elif wifi.internet == "activated":
icon_file = "wifi.svg"
tooltip = f"Connected to {wifi.ssid}"
if wifi.strength >= 0:
tooltip += f" ({wifi.strength}%)"
elif wifi.internet == "activating":
icon_file = "wifi.svg"
tooltip = f"Connecting to {wifi.ssid}..."
else:
icon_file = "wifi-off.svg"
tooltip = "WiFi disconnected"
elif primary_device == "wired" and self.network_service.ethernet_device:
ethernet = self.network_service.ethernet_device
if ethernet.internet == "activated":
icon_file = "ethernet.svg"
tooltip = "Ethernet connected"
if hasattr(ethernet, "speed") and ethernet.speed > 0:
tooltip += f" ({ethernet.speed} Mbps)"
elif ethernet.internet == "activating":
icon_file = "wifi.svg"
tooltip = "Ethernet connecting..."
else:
icon_file = "wifi-off.svg"
tooltip = "Ethernet disconnected"
self.network_icon.set_from_file(
get_relative_path(f"../../../config/assets/icons/{icon_file}")
)
self.network_button.set_tooltip_text(tooltip)
class BatteryIndicator(Box):
def __init__(self, **kwargs):
super().__init__(name="battery-indicator", orientation="h", **kwargs)
self.battery_service = Battery()
self.battery_icon = Svg(
name="battery-icon",
size=25,
svg_file=get_relative_path("../../../config/assets/icons/battery.svg"),
)
self.battery_button = Button(name="battery-button", child=self.battery_icon)
self.add(self.battery_button)
modus_service.connect("battery-changed", self.on_battery_changed)
self.battery_service.connect("changed", self.on_battery_direct_changed)
self.update_modus_service_battery_state()
self.update_state()
def on_battery_changed(self, service, new_battery_state):
self.update_state()
def on_battery_direct_changed(self, *args):
self.update_modus_service_battery_state()
self.update_state()
def update_modus_service_battery_state(self):
if not self.battery_service.is_present:
battery_state = "not_present"
else:
percentage = self.battery_service.percentage
state = self.battery_service.state.lower()
battery_state = f"{state}:{percentage}%"
if state == "discharging":
time_to_empty = self.battery_service.time_to_empty
if time_to_empty != "N/A":
battery_state += f":{time_to_empty}"
elif state == "charging":
time_to_full = self.battery_service.time_to_full
if time_to_full != "N/A":
battery_state += f":{time_to_full}"
modus_service.battery = battery_state
def get_battery_icon_level(self, percentage):
if percentage >= 90:
return "100"
elif percentage >= 80:
return "090"
elif percentage >= 70:
return "080"
elif percentage >= 60:
return "070"
elif percentage >= 50:
return "060"
elif percentage >= 40:
return "050"
elif percentage >= 30:
return "040"
elif percentage >= 20:
return "030"
elif percentage >= 10:
return "020"
else:
return "010"
def get_battery_icon_file(self, percentage, is_charging):
level = self.get_battery_icon_level(percentage)
suffix = "-charging" if is_charging else ""
return f"battery/battery-{level}{suffix}.svg"
def get_battery_tooltip(self, percentage, state):
tooltip = f"Battery: {percentage}%"
if state == "CHARGING":
time_to_full = self._battery.time_to_full
if time_to_full:
tooltip_points.append(f"• Time until full: {time_to_full}")
tooltip += " (Charging)"
time_to_full = self.battery_service.time_to_full
if time_to_full != "N/A":
tooltip += f" - {time_to_full} until full"
elif state == "DISCHARGING":
time_to_empty = self._battery.time_to_empty
if time_to_empty:
tooltip_points.append(f"• Time remaining: {time_to_empty}")
time_to_empty = self.battery_service.time_to_empty
if time_to_empty != "N/A":
tooltip += f" - {time_to_empty} remaining"
elif state == "FULLY_CHARGED":
tooltip += " (Fully charged)"
return "\n".join(tooltip_points)
return tooltip
def update_battery(self, *_):
if not self._battery.is_present:
self.set_visible(False)
return True
percentage = self._battery.percentage
state = self._battery.state
charging = state in ["CHARGING", "FULLY_CHARGED"]
if percentage <= 15 and not charging:
self.icon.add_style_class("alert")
def update_state(self):
if not self.battery_service.is_present:
icon_file = "battery.svg"
tooltip = "No battery detected"
else:
self.icon.remove_style_class("alert")
percentage = self.battery_service.percentage
state = self.battery_service.state
is_charging = state in ["CHARGING", "FULLY_CHARGED"]
if state == "FULLY_CHARGED" or (percentage >= 100 and state == "CHARGING"):
self.icon.set_from_file(
get_relative_path(
"../../../config/assets/icons/battery/battery-100-charging.svg"
)
)
elif state == "CHARGING":
# Calculate battery level and use corresponding charging icon
level = min(100, max(0, int(percentage // 10) * 10))
if level == 0 and percentage > 0:
level = 10
icon_name = f"battery-{level:03d}-charging.svg"
self.icon.set_from_file(
get_relative_path(f"../../../config/assets/icons/battery/{icon_name}")
)
else:
# Calculate battery level and use corresponding icon
level = min(100, max(0, int(percentage // 10) * 10))
if level == 0 and percentage > 0:
level = 10
icon_name = f"battery-{level:03d}.svg"
self.icon.set_from_file(
get_relative_path(f"../../../config/assets/icons/battery/{icon_name}")
)
icon_file = self.get_battery_icon_file(percentage, is_charging)
tooltip = self.get_battery_tooltip(percentage, state)
self.set_visible(True)
return True
class Indicators(Box):
def __init__(self, **kwargs):
super().__init__(
name="indicators",
orientation="h",
spacing=10,
children=[
Battery(),
WifiIndicator(),
BluetoothIndicator(),
],
**kwargs,
# Update icon and tooltip
self.battery_icon.set_from_file(
get_relative_path(f"../../../config/assets/icons/{icon_file}")
)
self.show_all()
self.battery_button.set_tooltip_text(tooltip)