Huge updates: ControlCenter, DropDown Menu
This commit is contained in:
34 files changed
+3261
-1440
No files matched your search
@@ -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)
|
||||
Reference in new issue
Block a user