diff --git a/config/assets/icons/applets/redshift-status-off.svg b/config/assets/icons/applets/redshift-status-off.svg new file mode 100644 index 0000000..e81d366 --- /dev/null +++ b/config/assets/icons/applets/redshift-status-off.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + diff --git a/config/assets/icons/applets/redshift-status-on.svg b/config/assets/icons/applets/redshift-status-on.svg new file mode 100644 index 0000000..aaa2959 --- /dev/null +++ b/config/assets/icons/applets/redshift-status-on.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + diff --git a/modules/controlcenter/main.py b/modules/controlcenter/main.py index 3a1643e..cd1d9fb 100644 --- a/modules/controlcenter/main.py +++ b/modules/controlcenter/main.py @@ -14,6 +14,7 @@ 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.nightlight import create_night_light_widget from modules.controlcenter.per_app_volume import PerAppVolumeControl from modules.controlcenter.player import PlayerBoxStack from modules.controlcenter.wifi import WifiConnections @@ -298,17 +299,18 @@ class ModusControlCenter(Window): self.flight_widget = Button( name="flight-widget", child=Box( - orientation="h", - spacing=4, - h_align="start", - v_align="center", + orientation="v", h_expand=True, + v_expand=True, + spacing=8, + h_align="center", + v_align="center", children=[ self.flight_icon, Label( label="Flight", - style_classes="title", - h_align="start", + style_classes="title-widget", + h_align="center", ), ], ), @@ -334,32 +336,37 @@ class ModusControlCenter(Window): self.caffeine_widget = Button( name="caffeine-widget", child=Box( - orientation="h", - spacing=4, - h_align="start", + orientation="v", + spacing=8, + h_expand=True, + v_expand=True, + h_align="center", v_align="center", children=[ self.caffeine_icon, - Box( - orientation="vertical", - v_expand=True, - v_align="center", - h_align="start", - h_expand=True, - children=[ - Label( - label="Caffeine", - style_classes="title-widget", - h_align="start", - ), - self.caffeine_status_label, - ], + Label( + label="Caffeine", + style_classes="title-widget", + h_align="center", ), + # Box( + # orientation="vertical", + # v_expand=True, + # v_align="center", + # h_align="center", + # h_expand=True, + # children=[ + # # self.caffeine_status_label, + # ], + # ), ], ), on_clicked=self.toggle_caffeine, ) + # Create night light widget + self.night_light_widget = create_night_light_widget(self) + # Create main widgets directly without XML self.widgets = Box( orientation="vertical", @@ -381,6 +388,7 @@ class ModusControlCenter(Window): children=[ self.wlan_widget, self.bluetooth_widget, + self.night_light_widget, ], ), # Right side: DND, Flight Mode, and Caffeine @@ -403,19 +411,21 @@ class ModusControlCenter(Window): orientation="horizontal", name="flight-caffeine-row", children=[ - # Box( - # orientation="vertical", - # name="flight-widget-container", - # style_classes="menu", - # h_expand=True, - # children=[ - # self.flight_widget, - # ], - # ), + Box( + orientation="vertical", + name="flight-widget-container", + style_classes="menu", + h_expand=True, + v_expand=True, + children=[ + self.flight_widget, + ], + ), Box( orientation="vertical", name="caffeine-widget-container", h_expand=True, + v_expand=True, style_classes="menu", children=[ self.caffeine_widget, diff --git a/modules/controlcenter/nightlight.py b/modules/controlcenter/nightlight.py new file mode 100644 index 0000000..c5414c2 --- /dev/null +++ b/modules/controlcenter/nightlight.py @@ -0,0 +1,146 @@ +# Standard library imports +import subprocess + +# Fabric imports +from fabric.utils.helpers import get_relative_path +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.label import Label +from fabric.widgets.svg import Svg + +# Local imports +from loguru import logger + + +class NightLightControl: + """Control night light using hyprsunset""" + + def __init__(self): + self.is_active = False + self._check_initial_state() + + def _check_initial_state(self): + """Check if hyprsunset is currently running""" + try: + result = subprocess.run( + ["pgrep", "-f", "hyprsunset"], capture_output=True, text=True + ) + self.is_active = bool(result.stdout.strip()) + except Exception as e: + logger.warning(f"Failed to check hyprsunset status: {e}") + self.is_active = False + + def toggle(self): + """Toggle night light on/off""" + try: + if self.is_active: + # Turn off night light by killing hyprsunset + subprocess.run(["pkill", "hyprsunset"], check=False) + self.is_active = False + logger.debug("Night light turned off") + else: + # Turn on night light with default temperature (3000K) + subprocess.Popen(["hyprsunset", "-t", "4500"], start_new_session=True) + self.is_active = True + logger.debug("Night light turned on") + return True + except Exception as e: + logger.warning(f"Failed to toggle night light: {e}") + return False + + def set_temperature(self, temperature: int): + """Set night light temperature (1000-6500K)""" + try: + # Kill existing process + subprocess.run(["pkill", "hyprsunset"], check=False) + + # Start with new temperature + subprocess.Popen( + ["hyprsunset", "-t", str(temperature)], start_new_session=True + ) + self.is_active = True + logger.debug(f"Night light temperature set to {temperature}K") + return True + except Exception as e: + logger.warning(f"Failed to set night light temperature: {e}") + return False + + +def create_night_light_widget(control_center): + """Create night light widget for control center""" + + # Initialize night light control + night_light = NightLightControl() + + # Create icon + night_light_icon = Svg( + name="nightlight-icon", + svg_file=get_relative_path( + "../../config/assets/icons/applets/redshift-status-on.svg" + if night_light.is_active + else "../../config/assets/icons/applets/redshift-status-off.svg" + ), + size=42, + ) + + # Create status label + night_light_status_label = Label( + label="On" if night_light.is_active else "Off", + name="nightlight-widget-label", + style_classes="status-label", + max_chars_width=15, + ellipsization="end", + h_align="start", + ) + + def toggle_night_light(*_): + """Toggle night light and update UI""" + if night_light.toggle(): + # Update icon + night_light_icon.set_from_file( + get_relative_path( + "../../config/assets/icons/applets/redshift-status-on.svg" + if night_light.is_active + else "../../config/assets/icons/applets/redshift-status-off.svg" + ) + ) + # Update status label + night_light_status_label.set_label("On" if night_light.is_active else "Off") + + # Create widget box (similar to bluetooth_widget structure) + night_light_widget = Box( + name="nightlight-widget", + orientation="h", + h_expand=True, + children=[ + Button( + name="nightlight-icon-button", + child=night_light_icon, + on_clicked=toggle_night_light, + ), + Button( + name="nightlight-info-button", + child=Box( + name="nightlight-widget-info", + h_expand=True, + v_expand=True, + v_align="center", + h_align="start", + orientation="vertical", + children=[ + Label( + name="nightlight-widget-name", + label="Night Light", + style_classes="ct", + h_align="start", + ), + night_light_status_label, + ], + ), + on_clicked=toggle_night_light, + ), + ], + ) + + return night_light_widget + diff --git a/modules/controlcenter/player.py b/modules/controlcenter/player.py index 6587b86..d5a6fd4 100644 --- a/modules/controlcenter/player.py +++ b/modules/controlcenter/player.py @@ -720,16 +720,17 @@ class PlayerBox(Box): # ], ) - self.box = Box( + self.box = CenterBox( name="box-c", orientation="h", h_align="center", - h_expand=True, - children=[ + start_children=[ self.outer_box, - self.button_box, # self.stack_buttons_box, ], + end_children=[ + self.button_box, + ], ) self.children = [ diff --git a/styles/controlcenter.css b/styles/controlcenter.css index 95bf359..a60738d 100644 --- a/styles/controlcenter.css +++ b/styles/controlcenter.css @@ -50,9 +50,10 @@ } /* Widgets */ #wifi-widget, -#bluetooth-widget { +#bluetooth-widget, +#nightlight-widget { min-width: 140px; - padding: 10px 0 0px 0; + padding: 5px 0 0px 0; } .icon { @@ -62,7 +63,8 @@ } #bluetooth-widget-label, -#wifi-widget-label { +#wifi-widget-label, +#nightlight-widget-label { font-size: 12px; margin-left: 5px; font-weight: 500; @@ -283,7 +285,8 @@ } #bluetooth-widget-name, -#wifi-widget-name { +#wifi-widget-name, +#nightlight-widget-name { margin-top: 6px; font-size: 14px; font-weight: bold; @@ -953,9 +956,9 @@ /* margin-right: 8px; */ } -#flight-icon { - margin-left: -10px; -} +/* #flight-icon { */ +/* margin-left: -10px; */ +/* } */ .title-widget { /* margin-top: -14px; */ @@ -978,3 +981,8 @@ #app-control-box { min-width: 100px; } +#caffeine-widget, +#flight-widget { + padding: 0px 0 5px 0; + min-width: 50px; +} diff --git a/styles/player.css b/styles/player.css index 8e6398d..bae0196 100644 --- a/styles/player.css +++ b/styles/player.css @@ -56,7 +56,7 @@ margin-bottom: 0.5rem; min-height: 50px; - min-width: 220px; + min-width: 330px; /* border-radius: 8px; */ /* margin: 0 6px 10px; */ /* border: 0.5px solid alpha(#000, 0.4); */