✨ feat(controlcenter): add flight mode and caffeine toggle functionality

This commit is contained in:
sankalp committed 2025-08-11 17:58:41 +05:45
1 parent c554b109f4
commit fccb44b208
6 files changed
+254 -20

No files matched your search

@@ -0,0 +1,13 @@
<svg width="24" height="24" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#f2f2f2; } .ColorScheme-Highlight { color:#4285f4; }
</style>
</defs>
<g transform="translate(4,4)" class="ColorScheme-Text" fill="currentColor">
<path d="m1 7v3c0 3 2 5 5 5h2c3 0 4.76-1.566 4.959-4h0.541c1.5 0 1.5-1.5 1.5-1.5v-1c0-1.5-1.5-1.5-1.5-1.5h-0.5zm1 1h10v2c0 2.216-1.784 4-4 4h-2c-2.216 0-4-1.784-4-4zm11.5 0c0.5-0.032 0.516 0.531 0.516 0.531v1c0 0.469-0.516 0.469-0.516 0.469h-0.5l0.01-1.985z"/>
<path d="m7.193-1e-3c-1.098 1.385-1.428 2.066-1.032 2.948 0.36 0.798 1.288 1.205 0.821 3.052 0.767-0.9 1.376-2.042 0.774-2.964-0.612-0.938-0.948-1.508-0.563-3.036z" opacity=".35"/>
<path d="m4.193-1e-3c-1.098 1.385-1.428 2.066-1.032 2.948 0.36 0.798 1.288 1.205 0.821 3.052 0.767-0.9 1.376-2.042 0.774-2.964-0.612-0.938-0.948-1.508-0.563-3.036z" opacity=".35"/>
<path d="m10.193-1e-3c-1.098 1.385-1.428 2.066-1.032 2.948 0.36 0.798 1.288 1.205 0.821 3.052 0.767-0.9 1.376-2.042 0.774-2.964-0.612-0.938-0.948-1.508-0.563-3.036z" opacity=".35"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,13 @@
<svg width="24" height="24" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#f2f2f2; } .ColorScheme-Highlight { color:#4285f4; }
</style>
</defs>
<g transform="translate(4,4)" class="ColorScheme-Text" fill="currentColor">
<path d="m1 7v3c0 3 2 5 5 5h2c3 0 4.76-1.566 4.959-4h0.541c1.5 0 1.5-1.5 1.5-1.5v-1c0-1.5-1.5-1.5-1.5-1.5h-0.5zm1 1h10v2c0 2.216-1.784 4-4 4h-2c-2.216 0-4-1.784-4-4zm11.5 0c0.5-0.032 0.516 0.531 0.516 0.531v1c0 0.469-0.516 0.469-0.516 0.469h-0.5l0.01-1.985z"/>
<path d="m7.193-1e-3c-1.098 1.385-1.428 2.066-1.032 2.948 0.36 0.798 1.288 1.205 0.821 3.052 0.767-0.9 1.376-2.042 0.774-2.964-0.612-0.938-0.948-1.508-0.563-3.036z"/>
<path d="m4.193-1e-3c-1.098 1.385-1.428 2.066-1.032 2.948 0.36 0.798 1.288 1.205 0.821 3.052 0.767-0.9 1.376-2.042 0.774-2.964-0.612-0.938-0.948-1.508-0.563-3.036z"/>
<path d="m10.193-1e-3c-1.098 1.385-1.428 2.066-1.032 2.948 0.36 0.798 1.288 1.205 0.821 3.052 0.767-0.9 1.376-2.042 0.774-2.964-0.612-0.938-0.948-1.508-0.563-3.036z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,12 @@
<svg width="22" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style id="current-color-scheme" type="text/css">.ColorScheme-Text {
color:#f2f2f2;
}
.ColorScheme-NegativeText{
color:#e74c3c;
}</style>
</defs>
<path class="ColorScheme-Text" d="m19.893 11c-0.0078-0.91918-1.262-1.6436-2.7965-1.6436h-3.2015c-0.43623 0-0.59984-0.0779-0.86466-0.36611l-5.2814-5.7098c-0.17137-0.18695-0.3739-0.28043-0.5998-0.28043h-1.0049c-0.20254 0-0.31937 0.18695-0.22591 0.39727l2.7342 5.9513-3.9961 0.444-1.4255-2.5394c-0.10906-0.19475-0.27264-0.28043-0.5297-0.28043h-0.35054c-0.21032 0-0.35053 0.14021-0.35053 0.35053v7.3534c0 0.21032 0.14022 0.35053 0.35053 0.35053h0.35054c0.25706 0 0.42064-0.0856 0.5297-0.28042l1.4255-2.5472 3.9961 0.45183-2.7342 5.9513c-0.09347 0.21031 0.02337 0.39732 0.22591 0.39732h1.0049c0.2259 0 0.42842-0.1013 0.5998-0.28042l5.2814-5.7098c0.26482-0.28822 0.42843-0.36613 0.86466-0.36613h3.2015c1.5345 0 2.7887-0.73225 2.7965-1.6436z" fill="currentColor" opacity=".35"/>
<rect class="ColorScheme-Text" transform="rotate(45)" x="4.5563" y="-.5" width="22" height="1" rx=".5" ry=".5" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,8 @@
<svg width="22" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style id="current-color-scheme" type="text/css">.ColorScheme-Text {
color:#f2f2f2;
}</style>
</defs>
<path class="ColorScheme-Text" d="m19.893 11c-0.0078-0.91918-1.262-1.6436-2.7965-1.6436h-3.2015c-0.43623 0-0.59984-0.0779-0.86466-0.36611l-5.2814-5.7098c-0.17137-0.18695-0.3739-0.28043-0.5998-0.28043h-1.0049c-0.20254 0-0.31937 0.18695-0.22591 0.39727l2.7342 5.9513-3.9961 0.444-1.4255-2.5394c-0.10906-0.19475-0.27264-0.28043-0.5297-0.28043h-0.35054c-0.21032 0-0.35053 0.14021-0.35053 0.35053v7.3534c0 0.21032 0.14022 0.35053 0.35053 0.35053h0.35054c0.25706 0 0.42064-0.0856 0.5297-0.28042l1.4255-2.5472 3.9961 0.45183-2.7342 5.9513c-0.09347 0.21031 0.02337 0.39732 0.22591 0.39732h1.0049c0.2259 0 0.42842-0.1013 0.5998-0.28042l5.2814-5.7098c0.26482-0.28822 0.42843-0.36613 0.86466-0.36613h3.2015c1.5345 0 2.7887-0.73225 2.7965-1.6436z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 971 B

+207 -19
View File
@@ -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()
+1 -1
View File
@@ -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); */