From fccb44b208a7f43ef7430b269de901d925dd32b1 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Mon, 11 Aug 2025 17:58:41 +0545 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(controlcenter):=20add=20flight?= =?UTF-8?q?=20mode=20and=20caffeine=20toggle=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../icons/applets/caffeine-cup-empty.svg | 13 + .../icons/applets/caffeine-cup-full.svg | 13 + .../icons/applets/network-flightmode-off.svg | 12 + .../icons/applets/network-flightmode-on.svg | 8 + modules/controlcenter/main.py | 226 ++++++++++++++++-- styles/player.css | 2 +- 6 files changed, 254 insertions(+), 20 deletions(-) create mode 100644 config/assets/icons/applets/caffeine-cup-empty.svg create mode 100644 config/assets/icons/applets/caffeine-cup-full.svg create mode 100644 config/assets/icons/applets/network-flightmode-off.svg create mode 100644 config/assets/icons/applets/network-flightmode-on.svg diff --git a/config/assets/icons/applets/caffeine-cup-empty.svg b/config/assets/icons/applets/caffeine-cup-empty.svg new file mode 100644 index 0000000..6175212 --- /dev/null +++ b/config/assets/icons/applets/caffeine-cup-empty.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/config/assets/icons/applets/caffeine-cup-full.svg b/config/assets/icons/applets/caffeine-cup-full.svg new file mode 100644 index 0000000..ea4a065 --- /dev/null +++ b/config/assets/icons/applets/caffeine-cup-full.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/config/assets/icons/applets/network-flightmode-off.svg b/config/assets/icons/applets/network-flightmode-off.svg new file mode 100644 index 0000000..ac3e7ce --- /dev/null +++ b/config/assets/icons/applets/network-flightmode-off.svg @@ -0,0 +1,12 @@ + + + + + + + diff --git a/config/assets/icons/applets/network-flightmode-on.svg b/config/assets/icons/applets/network-flightmode-on.svg new file mode 100644 index 0000000..5202b89 --- /dev/null +++ b/config/assets/icons/applets/network-flightmode-on.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/modules/controlcenter/main.py b/modules/controlcenter/main.py index 86f411b..60c60c9 100644 --- a/modules/controlcenter/main.py +++ b/modules/controlcenter/main.py @@ -1,4 +1,4 @@ -from gi.repository import Gdk, GLib +import subprocess from fabric.utils import idle_add from fabric.utils.helpers import ( @@ -6,30 +6,25 @@ from fabric.utils.helpers import ( ) from fabric.widgets.box import Box from fabric.widgets.button import Button -from loguru import logger from fabric.widgets.centerbox import CenterBox from fabric.widgets.label import Label from fabric.widgets.scale import Scale from fabric.widgets.svg import Svg -from fabric.widgets.revealer import Revealer +from gi.repository import Gdk, GLib +from loguru import logger from modules.controlcenter.bluetooth import BluetoothConnections +from modules.controlcenter.expanded_player import EmbeddedExpandedPlayer +from modules.controlcenter.per_app_volume import PerAppVolumeControl +from modules.controlcenter.player import PlayerBoxStack from modules.controlcenter.wifi import WifiConnections from services.brightness import Brightness -from utils.roam import audio_service, modus_service -from widgets.wayland import WaylandWindow as Window -from modules.controlcenter.player import PlayerBoxStack -from modules.controlcenter.per_app_volume import PerAppVolumeControl -from modules.controlcenter.expanded_player import EmbeddedExpandedPlayer from services.mpris import MprisPlayerManager from services.network import NetworkClient -from fabric.widgets.svg import Svg -from fabric.utils.helpers import get_relative_path +from utils.roam import audio_service, modus_service +from widgets.wayland import WaylandWindow as Window brightness_service = Brightness.get_initial() -# FIX: Icon not showing up in control center -# TODO: Add Player - class ModusControlCenter(Window): def __init__(self, **kwargs): @@ -48,6 +43,11 @@ class ModusControlCenter(Window): self._updating_brightness = False self._updating_volume = False + # Flight mode and caffeine states + self.flight_mode = False + self.caffeine_mode = False + self._caffeine_process = None + # Lazy loading flags self._music_initialized = False self._per_app_volume_initialized = False @@ -64,6 +64,9 @@ class ModusControlCenter(Window): self.network_service = NetworkClient() self.wifi_service = None + # Initialize flight mode and caffeine states + self._check_initial_states() + # Wait for network service to be ready self.add_keybinding("Escape", self.hide_controlcenter) @@ -149,7 +152,9 @@ class ModusControlCenter(Window): # Create placeholder music widget - lazy load content when needed self.music_widget = Box( - name="music-widget", h_align="start", children=[] # Empty initially + name="music-widget", + h_align="start", + children=[], # Empty initially ) self.has_bluetooth_open = False @@ -256,17 +261,77 @@ class ModusControlCenter(Window): on_clicked=self.set_dont_disturb, ) + self.flight_icon = Svg( + name="flight-icon", + svg_file=get_relative_path( + "../../config/assets/icons/applets/network-flightmode-on.svg" + if self.flight_mode + else "../../config/assets/icons/applets/network-flightmode-off.svg" + ), + size=24, + ) + + self.flight_widget = Button( + name="flight-widget", + child=Box( + orientation="vertical", + spacing=4, + h_align="center", + v_align="center", + children=[ + self.flight_icon, + Label( + label="Flight", + style_classes="title", + h_align="center", + ), + ], + ), + on_clicked=self.toggle_flight_mode, + ) + + self.caffeine_icon = Svg( + name="caffeine-icon", + svg_file=get_relative_path( + "../../config/assets/icons/applets/caffeine-cup-full.svg" + if self.caffeine_mode + else "../../config/assets/icons/applets/caffeine-cup-empty.svg" + ), + size=24, + ) + + self.caffeine_widget = Button( + name="caffeine-widget", + child=Box( + orientation="vertical", + spacing=4, + h_align="center", + v_align="center", + children=[ + self.caffeine_icon, + Label( + label="Caffeine", + style_classes="title", + h_align="center", + ), + ], + ), + on_clicked=self.toggle_caffeine, + ) + # Create main widgets directly without XML self.widgets = Box( orientation="vertical", h_expand=True, name="control-center-widgets", children=[ + # Top section: Wi-Fi + Bluetooth on left, DND + Flight Mode + Caffeine on right Box( orientation="horizontal", - name="top-widget", h_expand=True, + name="top-widget", children=[ + # Left side: Wi-Fi and Bluetooth Box( orientation="vertical", name="wb-widget", @@ -277,13 +342,44 @@ class ModusControlCenter(Window): self.bluetooth_widget, ], ), + # Right side: DND, Flight Mode, and Caffeine Box( - orientation="horizontal", - name="dnd-widget", - style_classes="menu", + orientation="vertical", h_expand=True, + name="right-side-widget", children=[ - self.focus_widget, + # DND widget + Box( + orientation="vertical", + name="dnd-widget", + style_classes="menu", + children=[ + self.focus_widget, + ], + ), + # Flight Mode and Caffeine row + Box( + orientation="horizontal", + name="flight-caffeine-row", + children=[ + Box( + orientation="vertical", + name="flight-widget-container", + style_classes="menu", + children=[ + self.flight_widget, + ], + ), + Box( + orientation="vertical", + name="caffeine-widget-container", + style_classes="menu", + children=[ + self.caffeine_widget, + ], + ), + ], + ), ], ), ], @@ -521,6 +617,25 @@ class ModusControlCenter(Window): ) self.expanded_player_center_box.set_size_request(300, -1) + def _check_initial_states(self): + # Check if caffeine is already running + try: + result = subprocess.run( + ["pgrep", "-f", "modus-inhibit"], capture_output=True, text=True + ) + self.caffeine_mode = bool(result.stdout.strip()) + except Exception: + self.caffeine_mode = False + + # Flight mode starts as False (normal mode) + self.flight_mode = False + + # Update initial labels (will be set after widgets are created) + GLib.timeout_add(100, self._update_initial_labels) + + def _update_initial_labels(self): + return False + def set_dont_disturb(self, *_): self.focus_mode = not self.focus_mode modus_service.dont_disturb = self.focus_mode @@ -532,6 +647,71 @@ class ModusControlCenter(Window): ) ) + def toggle_flight_mode(self, *_): + try: + self.flight_mode = not self.flight_mode + + if self.flight_mode: + # Turn off WiFi and Bluetooth + if self.wifi_service: + self.wifi_service.wireless_enabled = False + if hasattr(self, "bluetooth_man") and hasattr( + self.bluetooth_man, "client" + ): + self.bluetooth_man.client.set_enabled(False) + else: + # Turn on WiFi and Bluetooth + if self.wifi_service: + self.wifi_service.wireless_enabled = True + if hasattr(self, "bluetooth_man") and hasattr( + self.bluetooth_man, "client" + ): + self.bluetooth_man.client.set_enabled(True) + + # Update icon + self.flight_icon.set_from_file( + get_relative_path( + "../../config/assets/icons/applets/network-flightmode-on.svg" + if self.flight_mode + else "../../config/assets/icons/applets/network-flightmode-off.svg" + ) + ) + + except Exception as e: + logger.warning(f"Failed to toggle flight mode: {e}") + + def toggle_caffeine(self, *_): + try: + if self.caffeine_mode: + # Turn off caffeine + inhibit_script = get_relative_path("../../utils/inhibit.py") + subprocess.run(["python3", inhibit_script, "off"], check=False) + self.caffeine_mode = False + if self._caffeine_process: + try: + self._caffeine_process.terminate() + except: + pass + self._caffeine_process = None + else: + # Turn on caffeine + inhibit_script = get_relative_path("../../utils/inhibit.py") + self._caffeine_process = subprocess.Popen( + ["python3", inhibit_script, "on"], start_new_session=True + ) + self.caffeine_mode = True + + self.caffeine_icon.set_from_file( + get_relative_path( + "../../config/assets/icons/applets/caffeine-cup-full.svg" + if self.caffeine_mode + else "../../config/assets/icons/applets/caffeine-cup-empty.svg" + ) + ) + + except Exception as e: + logger.warning(f"Failed to toggle caffeine: {e}") + def set_volume(self, _, __, volume): self._updating_volume = True audio_service.speaker.volume = round(volume) @@ -803,6 +983,14 @@ class ModusControlCenter(Window): except: pass + # Clean up caffeine process + if self._caffeine_process: + try: + self._caffeine_process.terminate() + except: + pass + self._caffeine_process = None + # Clean up heavy components if hasattr(self, "wifi_man") and self.wifi_man: self.wifi_man.destroy() diff --git a/styles/player.css b/styles/player.css index 92a10b0..b948207 100644 --- a/styles/player.css +++ b/styles/player.css @@ -32,7 +32,7 @@ #outer-player-box-c { min-height: 55px; - min-width: 260px; + min-width: 282px; border-radius: 8px; /* margin: 0 6px; */ /* background-color: alpha(#000, 0.3); */