From 00111ebe9df3d1dc1cd3285227115cd4564c08a8 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Fri, 21 Feb 2025 21:04:42 +0545 Subject: [PATCH] huge updates --- modules/launcher/components/__init__.py | 1 + modules/launcher/components/applauncher.py | 5 + modules/launcher/components/bluetooth.py | 51 ++- modules/launcher/components/buttons.py | 396 ++++++++++++++++ modules/launcher/components/dashboard.py | 504 +++++++++++++++++++++ modules/launcher/components/wallpapers.py | 30 +- modules/launcher/launcher.py | 20 +- styles/bar.css | 81 +++- styles/dashboard.css | 143 ++++++ styles/launcher.css | 19 +- styles/main.css | 6 +- styles/notification.css | 3 + 12 files changed, 1199 insertions(+), 60 deletions(-) create mode 100644 modules/launcher/components/buttons.py create mode 100644 modules/launcher/components/dashboard.py create mode 100644 styles/dashboard.css diff --git a/modules/launcher/components/__init__.py b/modules/launcher/components/__init__.py index e542a03..8ea1e96 100644 --- a/modules/launcher/components/__init__.py +++ b/modules/launcher/components/__init__.py @@ -9,3 +9,4 @@ from .todo import * from .wallpapers import * from .wifi import * from .calendar import * +from .dashboard import * diff --git a/modules/launcher/components/applauncher.py b/modules/launcher/components/applauncher.py index 4e755ae..54dd1c1 100644 --- a/modules/launcher/components/applauncher.py +++ b/modules/launcher/components/applauncher.py @@ -81,6 +81,11 @@ class AppLauncher(Box): self.viewport.children = [] self.selected_index = -1 + if query.strip(): + self.launcher.dashboard.hide() + else: + self.launcher.dashboard.show() + if not query.strip(): return False diff --git a/modules/launcher/components/bluetooth.py b/modules/launcher/components/bluetooth.py index 68fb90a..0cf1704 100644 --- a/modules/launcher/components/bluetooth.py +++ b/modules/launcher/components/bluetooth.py @@ -6,6 +6,7 @@ from fabric.widgets.centerbox import CenterBox from fabric.widgets.scrolledwindow import ScrolledWindow from fabric.bluetooth import BluetoothClient, BluetoothDevice from snippets import MaterialIcon +# from .buttons import class BluetoothDeviceSlot(CenterBox): @@ -62,6 +63,15 @@ class BluetoothConnections(Box): orientation="vertical", **kwargs, ) + self.launcher = kwargs["launcher"] + + self.bluetooth_button = self.launcher.dashboard.bluetooth_button + self.bt_status_text = self.bluetooth_button.bluetooth_status_text + self.bt_status_button = self.bluetooth_button.bluetooth_status_button + self.bt_icon = self.bluetooth_button.bluetooth_icon + self.bt_label = self.bluetooth_button.bluetooth_label + self.bt_menu_button = self.bluetooth_button.bluetooth_menu_button + self.bt_menu_label = self.bluetooth_button.bluetooth_menu_label self.client = BluetoothClient(on_device_added=self.on_device_added) self.toggle_icon = MaterialIcon("bluetooth_disabled") @@ -79,14 +89,8 @@ class BluetoothConnections(Box): child=self.toggle_icon, on_clicked=lambda *_: self.client.toggle_power(), ) - self.client.notify("scanning") - self.client.notify("enabled") - self.client.connect( - "notify::enabled", - lambda *_: self.toggle_icon.set_label( - "bluetooth" if self.client.enabled else "bluetooth_disabled" - ), - ) + + self.client.connect("notify::enabled", lambda *_: self.status_label()) self.client.connect( "notify::scanning", lambda *_: self.scan_icon.set_label( @@ -120,6 +124,37 @@ class BluetoothConnections(Box): # to run notify closures thus display the status # without having to wait until an actual change + self.client.notify("scanning") + self.client.notify("enabled") + + def status_label(self): + print(self.client.enabled) + if self.client.enabled: + self.toggle_icon.set_label("bluetooth") + self.bt_status_text.set_label("Enabled") + for i in [ + self.bt_status_button, + self.bt_status_text, + self.bt_icon, + self.bt_label, + self.bt_menu_button, + self.bt_menu_label, + ]: + i.remove_style_class("disabled") + self.bt_icon.set_label("bluetooth") + else: + self.toggle_icon.set_label("bluetooth_disabled") + self.bt_status_text.set_label("Disabled") + for i in [ + self.bt_status_button, + self.bt_status_text, + self.bt_icon, + self.bt_label, + self.bt_menu_button, + self.bt_menu_label, + ]: + i.add_style_class("disabled") + self.bt_icon.set_label("bluetooth_disabled") def on_device_added(self, client: BluetoothClient, address: str): if not (device := client.get_device(address)): diff --git a/modules/launcher/components/buttons.py b/modules/launcher/components/buttons.py new file mode 100644 index 0000000..d19fb89 --- /dev/null +++ b/modules/launcher/components/buttons.py @@ -0,0 +1,396 @@ +import subprocess +from fabric.widgets.box import Box +from fabric.widgets.centerbox import CenterBox +from fabric.widgets.label import Label +from fabric.widgets.button import Button +from fabric.utils.helpers import exec_shell_command_async, exec_shell_command +import gi +from gi.repository import Gtk, Gdk, GLib # Added Gdk import +from fabric.utils import get_relative_path + +gi.require_version("Gtk", "3.0") +from snippets import MaterialIcon + + +def add_hover_cursor(widget): + # Add enter/leave events to change the cursor + widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK) + widget.connect( + "enter-notify-event", + lambda w, e: w.get_window().set_cursor( + Gdk.Cursor.new_from_name(w.get_display(), "pointer") + ) + if w.get_window() + else None, + ) + widget.connect( + "leave-notify-event", + lambda w, e: w.get_window().set_cursor(None) if w.get_window() else None, + ) + + +class NetworkButton(Box): + def __init__(self): + self.network_icon = MaterialIcon( + "wifi", + ) + self.network_icon.add_style_class("network-icon") + self.network_label = Label( + name="network-label", + label="Wi-Fi", + justification="left", + ) + self.network_label_box = Box(children=[self.network_label, Box(h_expand=True)]) + self.network_ssid = Label( + name="network-ssid", + label="Hello_World!!", + justification="left", + ) + self.network_ssid_box = Box(children=[self.network_ssid, Box(h_expand=True)]) + self.network_text = Box( + name="network-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.network_label_box, self.network_ssid_box], + ) + self.network_status_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.network_icon, self.network_text], + ) + self.network_status_button = Button( + name="network-status-button", + h_expand=True, + child=self.network_status_box, + ) + add_hover_cursor(self.network_status_button) # <-- Added hover + + self.network_menu_label = MaterialIcon("chevron_right") + self.network_menu_button = Button( + name="network-menu-button", + child=self.network_menu_label, + ) + add_hover_cursor(self.network_menu_button) # <-- Added hover + + super().__init__( + name="network-button", + orientation="h", + h_align="fill", + v_align="fill", + h_expand=True, + v_expand=True, + spacing=0, + children=[self.network_status_button, self.network_menu_button], + ) + + +class BluetoothButton(Box): + def __init__(self, launcher): + super().__init__( + name="bluetooth-button", + orientation="h", + h_align="fill", + v_align="fill", + h_expand=True, + v_expand=True, + ) + self.launcher = launcher + + self.bluetooth_icon = MaterialIcon("bluetooth") + self.bluetooth_label = Label( + name="bluetooth-label", + label="Bluetooth", + justification="left", + ) + self.bluetooth_label_box = Box( + children=[self.bluetooth_label, Box(h_expand=True)] + ) + self.bluetooth_status_text = Label( + name="bluetooth-status", + label="Disabled", + justification="left", + ) + self.bluetooth_status_box = Box( + children=[self.bluetooth_status_text, Box(h_expand=True)] + ) + self.bluetooth_text = Box( + orientation="v", + h_align="start", + v_align="center", + children=[self.bluetooth_label_box, self.bluetooth_status_box], + ) + self.bluetooth_status_container = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.bluetooth_icon, self.bluetooth_text], + ) + self.bluetooth_status_button = Button( + name="bluetooth-status-button", + h_expand=True, + child=self.bluetooth_status_container, + on_clicked=lambda *_: self.launcher.bluetooth.client.toggle_power(), + ) + add_hover_cursor(self.bluetooth_status_button) # <-- Added hover + + self.bluetooth_menu_label = MaterialIcon("chevron_right") + self.bluetooth_menu_button = Button( + name="bluetooth-menu-button", + on_clicked=lambda *_: self.launcher.open("bluetooth"), + child=self.bluetooth_menu_label, + ) + add_hover_cursor(self.bluetooth_menu_button) # <-- Added hover + + self.add(self.bluetooth_status_button) + self.add(self.bluetooth_menu_button) + + +class NightModeButton(Button): + def __init__(self): + self.night_mode_icon = MaterialIcon("mode_night") + self.night_mode_label = Label( + name="night-mode-label", + label="Night Mode", + justification="left", + ) + self.night_mode_label_box = Box( + children=[self.night_mode_label, Box(h_expand=True)] + ) + self.night_mode_status = Label( + name="night-mode-status", + label="Enabled", + justification="left", + ) + self.night_mode_status_box = Box( + children=[self.night_mode_status, Box(h_expand=True)] + ) + self.night_mode_text = Box( + name="night-mode-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.night_mode_label_box, self.night_mode_status_box], + ) + self.night_mode_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.night_mode_icon, self.night_mode_text], + ) + + super().__init__( + name="night-mode-button", + h_expand=True, + child=self.night_mode_box, + on_clicked=self.toggle_hyprsunset, + ) + add_hover_cursor(self) # <-- Added hover + + self.widgets = [ + self, + self.night_mode_label, + self.night_mode_status, + self.night_mode_icon, + ] + self.check_hyprsunset() + + def toggle_hyprsunset(self, *args): + """ + Toggle the 'hyprsunset' process: + - If running, kill it and mark as 'Disabled'. + - If not running, start it and mark as 'Enabled'. + """ + try: + subprocess.check_output(["pgrep", "hyprsunset"]) + exec_shell_command_async("pkill hyprsunset") + self.night_mode_status.set_label("Disabled") + for widget in self.widgets: + widget.add_style_class("disabled") + except subprocess.CalledProcessError: + exec_shell_command_async("hyprsunset -t 3500") + self.night_mode_status.set_label("Enabled") + for widget in self.widgets: + widget.remove_style_class("disabled") + + def check_hyprsunset(self, *args): + """ + Update the button state based on whether hyprsunset is running. + """ + try: + subprocess.check_output(["pgrep", "hyprsunset"]) + self.night_mode_status.set_label("Enabled") + for widget in self.widgets: + widget.remove_style_class("disabled") + except subprocess.CalledProcessError: + self.night_mode_status.set_label("Disabled") + for widget in self.widgets: + widget.add_style_class("disabled") + + +class CaffeineButton(Button): + def __init__(self): + self.caffeine_icon = MaterialIcon("coffee") + self.caffeine_label = Label( + name="caffeine-label", + label="Caffeine", + justification="left", + ) + self.caffeine_label_box = Box( + children=[self.caffeine_label, Box(h_expand=True)] + ) + self.caffeine_status = Label( + name="caffeine-status", + label="Enabled", + justification="left", + ) + self.caffeine_status_box = Box( + children=[self.caffeine_status, Box(h_expand=True)] + ) + self.caffeine_text = Box( + name="caffeine-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.caffeine_label_box, self.caffeine_status_box], + ) + self.caffeine_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.caffeine_icon, self.caffeine_text], + ) + super().__init__( + name="caffeine-button", + h_expand=True, + child=self.caffeine_box, + on_clicked=self.toggle_wlinhibit, + ) + add_hover_cursor(self) # <-- Added hover + + self.widgets = [ + self, + self.caffeine_label, + self.caffeine_status, + self.caffeine_icon, + ] + self.check_wlinhibit() + + def toggle_wlinhibit(self, *args): + """ + Toggle the 'wlinhibit' process: + - If running, kill it and mark as 'Disabled' (add 'disabled' class). + - If not running, start it and mark as 'Enabled' (remove 'disabled' class). + """ + try: + subprocess.check_output(["pgrep", "wlinhibit"]) + exec_shell_command_async("pkill wlinhibit") + self.caffeine_status.set_label("Disabled") + for i in self.widgets: + i.add_style_class("disabled") + except subprocess.CalledProcessError: + exec_shell_command_async("wlinhibit") + self.caffeine_status.set_label("Enabled") + for i in self.widgets: + i.remove_style_class("disabled") + + def check_wlinhibit(self, *args): + try: + subprocess.check_output(["pgrep", "wlinhibit"]) + self.caffeine_status.set_label("Enabled") + for i in self.widgets: + i.remove_style_class("disabled") + except subprocess.CalledProcessError: + self.caffeine_status.set_label("Disabled") + for i in self.widgets: + i.add_style_class("disabled") + + +class DarkModeButton(Button): + def __init__(self): + self.dark_mode_icon = MaterialIcon("contrast") + self.dark_mode_icon.add_style_class("iconss") + self.dark_mode_label = Label( + name="dark-mode-label", label="Dark Mode", justification="left" + ) + + self.dark_mode_label_box = Box( + children=[self.dark_mode_label, Box(h_expand=True)] + ) + self.dark_mode_status = Label( + name="dark-mode-status", label="Enabled", justification="left" + ) + self.dark_mode_status_box = Box( + children=[self.dark_mode_status, Box(h_expand=True)] + ) + self.dark_mode_text = Box( + name="dark-mode-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.dark_mode_label_box, self.dark_mode_status_box], + ) + self.dark_mode_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.dark_mode_icon, self.dark_mode_text], + ) + + super().__init__( + name="dark-mode-button", + h_expand=True, + child=self.dark_mode_box, + on_clicked=self.toggle_darkmode, + ) + add_hover_cursor(self) + + self.widgets = [ + self, + self.dark_mode_label, + self.dark_mode_status, + self.dark_mode_icon, + ] + self.check_darkmode() + + def toggle_darkmode(self, *_): + current_state = self.check_darkmode() + new_mode = "dark" if not current_state else "light" + command = f"bash {get_relative_path('../../../config/scripts/dark-theme.sh')} --set {new_mode}" + GLib.spawn_command_line_async(command) + self.dark_mode_status.set_label("Enabled" if new_mode == "dark" else "Disabled") + + def check_darkmode(self): + result = exec_shell_command( + "gsettings get org.gnome.desktop.interface color-scheme" + ) + return result.strip().replace("'", "") == "prefer-dark" + + +class Buttons(Gtk.Grid): + def __init__(self, **kwargs): + super().__init__(name="buttons-grid") + self.set_row_homogeneous(True) + self.set_column_homogeneous(True) + self.set_row_spacing(8) + self.set_column_spacing(8) + self.set_vexpand(True) # Prevent vertical expansion + + self.launcher = kwargs["launcher"] + + # Instantiate each button + self.network_button = NetworkButton() + self.bluetooth_button = BluetoothButton(self.launcher) + self.night_mode_button = NightModeButton() + self.caffeine_button = CaffeineButton() + self.dark_mode_button = DarkModeButton() + + # Attach buttons into the grid (one row, four columns) + self.attach(self.network_button, 0, 0, 1, 1) + self.attach(self.bluetooth_button, 1, 0, 1, 1) + self.attach(self.dark_mode_button, 2, 0, 1, 1) + self.attach(self.night_mode_button, 0, 1, 1, 1) + self.attach(self.caffeine_button, 1, 1, 1, 1) + + self.show_all() diff --git a/modules/launcher/components/dashboard.py b/modules/launcher/components/dashboard.py new file mode 100644 index 0000000..246bfde --- /dev/null +++ b/modules/launcher/components/dashboard.py @@ -0,0 +1,504 @@ +import subprocess +from fabric.widgets.box import Box +from fabric.widgets.centerbox import CenterBox +from fabric.widgets.label import Label +from fabric.widgets.button import Button +from fabric.utils.helpers import exec_shell_command_async, exec_shell_command +import gi +from gi.repository import Gtk, Gdk, GLib # Added Gdk import +from fabric.utils import get_relative_path + +gi.require_version("Gtk", "3.0") +from snippets import MaterialIcon + + +def add_hover_cursor(widget): + # Add enter/leave events to change the cursor + widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK) + widget.connect( + "enter-notify-event", + lambda w, e: w.get_window().set_cursor( + Gdk.Cursor.new_from_name(w.get_display(), "pointer") + ) + if w.get_window() + else None, + ) + widget.connect( + "leave-notify-event", + lambda w, e: w.get_window().set_cursor(None) if w.get_window() else None, + ) + + +class NetworkButton(Box): + def __init__(self): + self.network_icon = MaterialIcon( + "wifi", + ) + self.network_label = Label( + name="network-label", + label="Wi-Fi", + justification="left", + ) + self.network_label_box = Box(children=[self.network_label, Box(h_expand=True)]) + self.network_ssid = Label( + name="network-ssid", + label="Hello_World!!", + justification="left", + ) + self.network_ssid_box = Box(children=[self.network_ssid, Box(h_expand=True)]) + self.network_text = Box( + name="network-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.network_label_box, self.network_ssid_box], + ) + self.network_status_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.network_icon, self.network_text], + ) + self.network_status_button = Button( + name="network-status-button", + h_expand=True, + child=self.network_status_box, + ) + add_hover_cursor(self.network_status_button) # <-- Added hover + + self.network_menu_label = MaterialIcon("chevron_right") + self.network_menu_button = Button( + name="network-menu-button", + child=self.network_menu_label, + ) + add_hover_cursor(self.network_menu_button) # <-- Added hover + + super().__init__( + name="network-button", + orientation="h", + h_align="fill", + v_align="fill", + h_expand=True, + v_expand=True, + spacing=0, + children=[self.network_status_button, self.network_menu_button], + ) + + +class BluetoothButton(Box): + def __init__(self, launcher): + super().__init__( + name="bluetooth-button", + orientation="h", + h_align="fill", + v_align="fill", + h_expand=True, + v_expand=True, + ) + self.launcher = launcher + + self.bluetooth_icon = MaterialIcon("bluetooth") + self.bluetooth_label = Label( + name="bluetooth-label", + label="Bluetooth", + justification="left", + ) + self.bluetooth_label_box = Box( + children=[self.bluetooth_label, Box(h_expand=True)] + ) + self.bluetooth_status_text = Label( + name="bluetooth-status", + label="Disabled", + justification="left", + ) + self.bluetooth_status_box = Box( + children=[self.bluetooth_status_text, Box(h_expand=True)] + ) + self.bluetooth_text = Box( + orientation="v", + h_align="start", + v_align="center", + children=[self.bluetooth_label_box, self.bluetooth_status_box], + ) + self.bluetooth_status_container = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.bluetooth_icon, self.bluetooth_text], + ) + self.bluetooth_status_button = Button( + name="bluetooth-status-button", + h_expand=True, + child=self.bluetooth_status_container, + on_clicked=lambda *_: self.launcher.bluetooth.client.toggle_power(), + ) + add_hover_cursor(self.bluetooth_status_button) # <-- Added hover + + self.bluetooth_menu_label = MaterialIcon("chevron_right") + self.bluetooth_menu_button = Button( + name="bluetooth-menu-button", + on_clicked=lambda *_: self.launcher.open("bluetooth"), + child=self.bluetooth_menu_label, + ) + add_hover_cursor(self.bluetooth_menu_button) # <-- Added hover + + self.add(self.bluetooth_status_button) + self.add(self.bluetooth_menu_button) + + +class NightModeButton(Button): + def __init__(self): + self.night_mode_icon = MaterialIcon("mode_night") + self.night_mode_label = Label( + name="night-mode-label", + label="Night Mode", + justification="left", + ) + self.night_mode_label_box = Box( + children=[self.night_mode_label, Box(h_expand=True)] + ) + self.night_mode_status = Label( + name="night-mode-status", + label="Enabled", + justification="left", + ) + self.night_mode_status_box = Box( + children=[self.night_mode_status, Box(h_expand=True)] + ) + self.night_mode_text = Box( + name="night-mode-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.night_mode_label_box, self.night_mode_status_box], + ) + self.night_mode_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.night_mode_icon, self.night_mode_text], + ) + + super().__init__( + name="night-mode-button", + h_expand=True, + child=self.night_mode_box, + on_clicked=self.toggle_hyprsunset, + ) + add_hover_cursor(self) # <-- Added hover + + self.widgets = [ + self, + self.night_mode_label, + self.night_mode_status, + self.night_mode_icon, + ] + self.check_hyprsunset() + + def toggle_hyprsunset(self, *args): + """ + Toggle the 'hyprsunset' process: + - If running, kill it and mark as 'Disabled'. + - If not running, start it and mark as 'Enabled'. + """ + try: + subprocess.check_output(["pgrep", "hyprsunset"]) + exec_shell_command_async("pkill hyprsunset") + self.night_mode_status.set_label("Disabled") + for widget in self.widgets: + widget.add_style_class("disabled") + except subprocess.CalledProcessError: + exec_shell_command_async("hyprsunset -t 3500") + self.night_mode_status.set_label("Enabled") + for widget in self.widgets: + widget.remove_style_class("disabled") + + def check_hyprsunset(self, *args): + """ + Update the button state based on whether hyprsunset is running. + """ + try: + subprocess.check_output(["pgrep", "hyprsunset"]) + self.night_mode_status.set_label("Enabled") + for widget in self.widgets: + widget.remove_style_class("disabled") + except subprocess.CalledProcessError: + self.night_mode_status.set_label("Disabled") + for widget in self.widgets: + widget.add_style_class("disabled") + + +class CaffeineButton(Button): + def __init__(self): + self.caffeine_icon = MaterialIcon("coffee") + self.caffeine_label = Label( + name="caffeine-label", + label="Caffeine", + justification="left", + ) + self.caffeine_label_box = Box( + children=[self.caffeine_label, Box(h_expand=True)] + ) + self.caffeine_status = Label( + name="caffeine-status", + label="Enabled", + justification="left", + ) + self.caffeine_status_box = Box( + children=[self.caffeine_status, Box(h_expand=True)] + ) + self.caffeine_text = Box( + name="caffeine-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.caffeine_label_box, self.caffeine_status_box], + ) + self.caffeine_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.caffeine_icon, self.caffeine_text], + ) + super().__init__( + name="caffeine-button", + h_expand=True, + child=self.caffeine_box, + on_clicked=self.toggle_wlinhibit, + ) + add_hover_cursor(self) # <-- Added hover + + self.widgets = [ + self, + self.caffeine_label, + self.caffeine_status, + self.caffeine_icon, + ] + self.check_wlinhibit() + + def toggle_wlinhibit(self, *args): + """ + Toggle the 'wlinhibit' process: + - If running, kill it and mark as 'Disabled' (add 'disabled' class). + - If not running, start it and mark as 'Enabled' (remove 'disabled' class). + """ + try: + subprocess.check_output(["pgrep", "wlinhibit"]) + exec_shell_command_async("pkill wlinhibit") + self.caffeine_status.set_label("Disabled") + for i in self.widgets: + i.add_style_class("disabled") + except subprocess.CalledProcessError: + exec_shell_command_async("wlinhibit") + self.caffeine_status.set_label("Enabled") + for i in self.widgets: + i.remove_style_class("disabled") + + def check_wlinhibit(self, *args): + try: + subprocess.check_output(["pgrep", "wlinhibit"]) + self.caffeine_status.set_label("Enabled") + for i in self.widgets: + i.remove_style_class("disabled") + except subprocess.CalledProcessError: + self.caffeine_status.set_label("Disabled") + for i in self.widgets: + i.add_style_class("disabled") + + +class DarkModeButton(Button): + def __init__(self): + self.dark_mode_icon = MaterialIcon("contrast") + self.dark_mode_label = Label( + name="dark-mode-label", label="Dark Mode", justification="left" + ) + + self.dark_mode_label_box = Box( + children=[self.dark_mode_label, Box(h_expand=True)] + ) + self.dark_mode_status = Label( + name="dark-mode-status", label="Enabled", justification="left" + ) + self.dark_mode_status_box = Box( + children=[self.dark_mode_status, Box(h_expand=True)] + ) + self.dark_mode_text = Box( + name="dark-mode-text", + orientation="v", + h_align="start", + v_align="center", + children=[self.dark_mode_label_box, self.dark_mode_status_box], + ) + self.dark_mode_box = Box( + h_align="start", + v_align="center", + spacing=10, + children=[self.dark_mode_icon, self.dark_mode_text], + ) + + super().__init__( + name="dark-mode-button", + h_expand=True, + child=self.dark_mode_box, + on_clicked=self.toggle_darkmode, + ) + add_hover_cursor(self) + + self.widgets = [ + self, + self.dark_mode_label, + self.dark_mode_status, + self.dark_mode_icon, + ] + self.check_darkmode() + + def toggle_darkmode(self, *_): + current_state = self.check_darkmode() + new_mode = "dark" if not current_state else "light" + command = f"bash {get_relative_path('../../../config/scripts/dark-theme.sh')} --set {new_mode}" + GLib.spawn_command_line_async(command) + + self.dark_mode_status.set_label("Enabled" if new_mode == "dark" else "Disabled") + + # Apply or remove 'disabled' class + if new_mode == "light": + for widget in self.widgets: + widget.add_style_class("disabled") + else: + for widget in self.widgets: + widget.remove_style_class("disabled") + + def check_darkmode(self): + result = exec_shell_command( + "gsettings get org.gnome.desktop.interface color-scheme" + ) + is_dark_mode = result.strip().replace("'", "") == "prefer-dark" + + if not is_dark_mode: + for widget in self.widgets: + widget.add_style_class("disabled") + else: + for widget in self.widgets: + widget.remove_style_class("disabled") + + return is_dark_mode + + +# +# class PowerProfileButton(Button): +# def __init__(self): +# self.power_profile_icon = MaterialIcon("local_fire_department") +# self.power_profile_label = Label( +# name="power-profile-label", label="Power Profile", justification="left" +# ) +# self.power_profile_label_box = Box( +# children=[self.power_profile_label, Box(h_expand=True)] +# ) +# self.power_profile_status = Label( +# name="power-profile-status", label="Loading...", justification="left" +# ) +# self.power_profile_status_box = Box( +# children=[self.power_profile_status, Box(h_expand=True)] +# ) +# self.power_profile_text = Box( +# name="power-profile-text", +# orientation="v", +# h_align="start", +# v_align="center", +# children=[self.power_profile_label_box, self.power_profile_status_box], +# ) +# self.power_profile_box = Box( +# h_align="start", +# v_align="center", +# spacing=10, +# children=[self.power_profile_icon, self.power_profile_text], +# ) +# +# super().__init__( +# name="power-profile-button", +# h_expand=True, +# child=self.power_profile_box, +# on_clicked=self.toggle_power_profile, +# ) +# add_hover_cursor(self) +# +# self.widgets = [ +# self, +# self.power_profile_label, +# self.power_profile_status, +# self.power_profile_icon, +# ] +# +# # **Delay the profile check to allow system services to start** +# GLib.timeout_add(1000, self.check_power_profile) +# +# def toggle_power_profile(self, *_): +# current_profile = self.check_power_profile() +# if current_profile == "balanced": +# self.set_power_profile("performance") +# elif current_profile == "performance": +# self.set_power_profile("power-saver") +# else: +# self.set_power_profile("balanced") +# +# def check_power_profile(self): +# try: +# result = exec_shell_command("powerprofilesctl get") +# if isinstance(result, str): +# current_profile = result.strip().lower() +# self.power_profile_status.set_label(current_profile.capitalize()) +# self.update_button_style(current_profile) +# return current_profile +# except Exception as e: +# print(f"Error checking power profile: {e}") +# self.power_profile_status.set_label("Balanced") # Fallback +# return "balanced" +# +# def set_power_profile(self, profile): +# exec_shell_command_async(f"powerprofilesctl set {profile}") +# self.power_profile_status.set_label(profile.capitalize()) +# self.update_button_style(profile) +# +# def update_button_style(self, profile): +# if profile == "balanced": +# self.power_profile_icon.set_label("balance") +# self.add_style_class("balanced-profile") +# self.power_profile_label.add_style_class("disabled") +# else: +# self.remove_style_class("balanced-profile") +# +# if profile == "performance": +# self.power_profile_icon.set_label("local_fire_department") +# elif profile == "power-saver": +# self.power_profile_icon.set_label("data_saver_on") +# + + +class Dashboard(Gtk.Grid): + def __init__(self, **kwargs): + super().__init__(name="buttons-grid") + self.set_row_homogeneous(True) + self.set_column_homogeneous(True) + self.set_row_spacing(8) + self.set_column_spacing(8) + self.set_vexpand(True) # Prevent vertical expansion + + self.launcher = kwargs["launcher"] + + # Instantiate each button + self.network_button = NetworkButton() + self.bluetooth_button = BluetoothButton(self.launcher) + self.night_mode_button = NightModeButton() + self.caffeine_button = CaffeineButton() + self.dark_mode_button = DarkModeButton() + # self.power_profile_button = PowerProfileButton() + + # Attach buttons into the grid (one row, four columns) + self.attach(self.network_button, 0, 0, 1, 1) + self.attach(self.bluetooth_button, 1, 0, 1, 1) + self.attach(self.dark_mode_button, 2, 0, 1, 1) + self.attach(self.night_mode_button, 0, 1, 1, 1) + self.attach(self.caffeine_button, 1, 1, 1, 1) + # self.attach(self.power_profile_button, 2, 1, 1, 1) + + self.show_all() diff --git a/modules/launcher/components/wallpapers.py b/modules/launcher/components/wallpapers.py index 0085c97..b777155 100644 --- a/modules/launcher/components/wallpapers.py +++ b/modules/launcher/components/wallpapers.py @@ -4,12 +4,10 @@ import os from PIL import Image from fabric.widgets.box import Box from fabric.widgets.centerbox import CenterBox -from fabric.widgets.button import Button from fabric.widgets.entry import Entry from fabric.widgets.scrolledwindow import ScrolledWindow from gi.repository import GdkPixbuf, GLib, Gtk, Gio, Gdk -from snippets import MaterialIcon -from fabric.utils import exec_shell_command, get_relative_path +from fabric.utils import get_relative_path import concurrent.futures from concurrent.futures import ThreadPoolExecutor from snippets import WALLPAPERS_DIR @@ -84,13 +82,6 @@ class WallpaperSelector(Box): self.scheme_dropdown.set_active_id("tonalSpot") self.scheme_dropdown.connect("changed", self.on_scheme_changed) - initial_icon = "light_mode" if not self.check_dark_mode_state() else "dark_mode" - self.toggle_button = Button( - child=MaterialIcon(initial_icon), - name="toggle-launcher-button", - on_clicked=self.toggle_dark_mode, - ) - self.dropdown_box = Box( name="dropdown-box", children=[ @@ -103,7 +94,7 @@ class WallpaperSelector(Box): spacing=10, orientation="h", start_children=[self.search_entry], - end_children=[self.dropdown_box, self.toggle_button], + end_children=[self.dropdown_box], ) self.add(self.header_box) @@ -335,20 +326,3 @@ class WallpaperSelector(Box): if self.get_mapped(): widget.grab_focus() return False - - def toggle_dark_mode(self, *_): - current_state = self.check_dark_mode_state() - new_mode = "dark" if not current_state else "light" - command = f"bash {get_relative_path('../../../config/scripts/dark-theme.sh')} --set {new_mode}" - GLib.spawn_command_line_async(command) - icon_name = "dark_mode" if new_mode == "dark" else "light_mode" - if self.toggle_button.get_child(): - self.toggle_button.remove(self.toggle_button.get_child()) - self.toggle_button.add(MaterialIcon(icon_name)) - self.toggle_button.show_all() - - def check_dark_mode_state(self): - result = exec_shell_command( - "gsettings get org.gnome.desktop.interface color-scheme" - ) - return result.strip().replace("'", "") == "prefer-dark" diff --git a/modules/launcher/launcher.py b/modules/launcher/launcher.py index d672865..6e484f6 100644 --- a/modules/launcher/launcher.py +++ b/modules/launcher/launcher.py @@ -1,6 +1,8 @@ from fabric.widgets.centerbox import CenterBox from fabric.widgets.stack import Stack from fabric.widgets.wayland import WaylandWindow as Window +from fabric.widgets.box import Box +from fabric.widgets.label import Label from modules.launcher.components import ( AppLauncher, BluetoothConnections, @@ -12,6 +14,7 @@ from modules.launcher.components import ( WallpaperSelector, WifiManager, Calendar, + Dashboard, ) @@ -25,17 +28,27 @@ class Launcher(Window): **kwargs, ) - self.launcher = AppLauncher(launcher=self) + self.dashboard = Dashboard(launcher=self) self.wallpapers = WallpaperSelector(launcher=self) self.power = PowerMenu(launcher=self) self.emoji = Emoji(launcher=self) self.cliphist = Cliphist(launcher=self) self.todo = TodoManager() - self.bluetooth = BluetoothConnections() + self.bluetooth = BluetoothConnections(launcher=self) self.sh = Sh(launcher=self) self.wifi = WifiManager() self.calendar = Calendar() + self.dashboard = Box( + name="dashboard", + orientation="h", + spacing=10, + children=[ + self.dashboard, + ], + ) + self.launcher = AppLauncher(launcher=self) + self.stack = Stack( name="launcher-content", v_expand=True, @@ -60,6 +73,7 @@ class Launcher(Window): name="launcher", orientation="v", start_children=self.stack, + end_children=self.dashboard, ) self.add(self.launcher_box) @@ -121,6 +135,7 @@ class Launcher(Window): for w in widgets.values(): w.hide() + self.dashboard.hide() for style in widgets.keys(): self.stack.remove_style_class(style) @@ -133,6 +148,7 @@ class Launcher(Window): self.launcher.open_launcher() self.launcher.search_entry.set_text("") self.launcher.search_entry.grab_focus() + self.dashboard.show() elif widget == "wallpapers": self.wallpapers.search_entry.set_text("") diff --git a/styles/bar.css b/styles/bar.css index 95edb20..f4d1ca6 100644 --- a/styles/bar.css +++ b/styles/bar.css @@ -51,18 +51,87 @@ /* * ── battery ─────────────────────────────────────────────────────────── */ +/*#battery {*/ +/* background-color: @surfaceContainer;*/ +/* border-radius: 100px;*/ +/* padding: 2px 10px 2px 10px;*/ +/*}*/ +/**/ +/*#battery label {*/ +/* font-weight: 600;*/ +/*}*/ +/**/ +/*#battery-label {*/ +/* margin-right: 5px;*/ +/*}*/ + #battery { background-color: @surfaceContainer; - border-radius: 100px; - padding: 2px 10px 2px 10px; + padding: 4px; + border-radius: 16px; + margin-top: 4px; } -#battery label { - font-weight: 600; +#battery-circle { + color: @surfaceBright; + border: 3px solid @primary; } -#battery-label { - margin-right: 5px; +#battery-circle.alert { + border: 2px solid @error-dim; +} + +#battery-icon { + font-size: 20px; +} + +#battery-icon.alert { + color: @error-dim; +} + +#battery-save, +#battery-balanced, +#battery-performance { + border-radius: 12px; + min-width: 28px; + min-height: 28px; +} + +#battery-save-label, +#battery-balanced-label, +#battery-performance-label { + color: @outline; + font-size: 20px; +} + +#battery-save:hover, +#battery-balanced:hover, +#battery-performance:hover { + background-color: @surfaceBright; +} + +#battery-save:hover #battery-save-label, +#battery-balanced:hover #battery-balanced-label, +#battery-performance:hover #battery-performance-label { + color: @primary; +} + +#battery-save.active, +#battery-balanced.active, +#battery-performance.active { + background-color: @primary; +} + +#battery-save.active #battery-save-label, +#battery-balanced.active #battery-balanced-label, +#battery-performance.active #battery-performance-label { + color: @shadow; +} + +#battery-level { + color: @primary; + font-weight: bold; + margin: 0 4px; } /* diff --git a/styles/dashboard.css b/styles/dashboard.css new file mode 100644 index 0000000..7857729 --- /dev/null +++ b/styles/dashboard.css @@ -0,0 +1,143 @@ +#buttons-grid { + min-height: 52px; +} + +#network-button, +#bluetooth-button, +#night-mode-button, +#caffeine-button, +#dark-mode-button, +#power-profile-button { + border-radius: 20px; +} + +#network-button, +#bluetooth-button { + background-color: unset; + padding: 0; +} + +#night-mode-button, +#caffeine-button, +#dark-mode-button, +#power-profile-button { + background-color: @primary; + padding: 0 10px; +} + +#night-mode-button.disabled, +#caffeine-button.disabled, +#dark-mode-button.disabled, +#power-profile.disabled { + background-color: @surface; +} + +#network-status-button, +#network-menu-button, +#bluetooth-status-button, +#bluetooth-menu-button { + background-color: @primary; + padding: 0 8px; +} + +#network-status-button:hover, +#network-menu-button:hover, +#bluetooth-status-button:hover, +#bluetooth-menu-button:hover, +#bluetooth-button:hover, +#night-mode-button:hover, +#caffeine-button:hover, +#dark-mode-button:hover, +#power-profile-button:hover { + background-color: mix(@onPrimary, @primary, 0.7); +} + +#network-status-button.disabled, +#network-menu-button.disabled, +#bluetooth-status-button.disabled, +#bluetooth-menu-button.disabled, +#dark-mode-button.disabled, +#power-profile-button.disabled { + background-color: @surface; +} + +#network-status-button.disabled:hover, +#network-menu-button.disabled:hover, +#bluetooth-status-button.disabled:hover, +#bluetooth-menu-button.disabled:hover, +#night-mode-button.disabled:hover, +#caffeine-button.disabled:hover, +#dark-mode-button.disabled:hover, +#power-profile-button.disabled:hover { + background-color: @surfaceBright; +} + +#network-status-button, +#bluetooth-status-button, +#dark-mode-status-button, +#power-profile-status-button { + border-radius: 20px 0 0 20px; +} + +#network-menu-button, +#bluetooth-menu-button { + border-radius: 0 20px 20px 0; +} + +#network-menu-label, +#bluetooth-menu-label, +#dark-mode-label, +#power-profile-label { + font-size: 16px; +} + +#network-menu-label.disabled, +#bluetooth-menu-label.disabled, +#dark-mode-label.disabled, +#power-profile-label.disabled { + color: @outline; +} + +#network-label, +#bluetooth-label, +#night-mode-label, +#caffeine-label, +#dark-mode-label, +#power-profile-label { + color: @shadow; + font-size: 14px; + font-weight: bold; + margin-bottom: -4px; +} + +#network-label.disabled, +#bluetooth-label.disabled, +#night-mode-label.disabled, +#caffeine-label.disabled, +#dark-mode-label.disabled, +#power-profile-label.disabled { + color: @outline; +} + +#network-ssid, +#bluetooth-status, +#night-mode-status, +#caffeine-status, +#dark-mode-status, +#power-profile-status { + color: @surfaceBright; + font-size: 12px; +} + +#network-ssid.disabled, +#bluetooth-status.disabled, +#night-mode-status.disabled, +#caffeine-status.disabled, +#dark-mode-status.disabled, +#power-profile-status.disabled { + color: @outline; +} + +#power-profile-button.balanced-profile { + background-color: transparent; +} diff --git a/styles/launcher.css b/styles/launcher.css index db2e522..9057c92 100644 --- a/styles/launcher.css +++ b/styles/launcher.css @@ -1,6 +1,9 @@ /* * ── launcher ────────────────────────────────────────────────────────── */ +#dashboard { + min-height: 100px; +} #launcher { margin: 18px; @@ -31,12 +34,12 @@ #launcher-content.bluetooth { min-height: 464px; - min-width: 464px; + min-width: 485px; } #launcher-content.wifi { min-height: 464px; - min-width: 464px; + min-width: 485px; } #launcher-content.power { @@ -49,7 +52,7 @@ } #launcher-content { - min-width: 420px; + min-width: 485px; } #app-launcher, @@ -63,16 +66,6 @@ transition: all 0.25s cubic-bezier(0.5, 0.25, 0, 1); } -#toggle-launcher-button { - background-color: @secondaryContainer; - color: @onSecondaryContainer; - margin: 11px; - margin-left: 0px; - min-width: 48px; - min-height: 44px; - border-radius: 100%; -} - #clear-launcher-button { background-color: @secondaryContainer; color: @onSecondaryContainer; diff --git a/styles/main.css b/styles/main.css index a971d02..7703a10 100644 --- a/styles/main.css +++ b/styles/main.css @@ -3,6 +3,7 @@ @import "./notification.css"; @import "./osd.css"; @import "./launcher.css"; +@import "./dashboard.css"; * { all: unset; @@ -61,9 +62,8 @@ tooltip label { /* * ── icon ────────────────────────────────────────────────────────────── */ - -#material_icon, -#material_icon * { +#material-icon, +#material-icon * { all: unset; font-family: "Material Symbols Outlined", "Material Symbols Sharp"; } diff --git a/styles/notification.css b/styles/notification.css index b1ba83c..16f1817 100644 --- a/styles/notification.css +++ b/styles/notification.css @@ -20,6 +20,8 @@ #notification .close-button { border-radius: 100px; + min-width: 40px; + min-height: 40px; } #notification .close-button:hover { @@ -42,6 +44,7 @@ border-radius: 100px; background-color: transparent; } + #notification-action-buttons button:hover { background-color: lighter(@outlineVariant); }