diff --git a/.gitignore b/.gitignore index e68a251..d6360f0 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ config/assets/screentime.json config/hypr/modus.conf config/hypr/colors.conf styles/colors.css +gray/* +gray diff --git a/config/hypr/colors.conf b/config/hypr/colors.conf index 12c8608..7ad1c79 100644 --- a/config/hypr/colors.conf +++ b/config/hypr/colors.conf @@ -1,27 +1,27 @@ -$wallpaper = /home/sankalp/Pictures/wallpapers/77.jpg -$background = 121318 -$foreground = e2e2e9 +$wallpaper = /home/sankalp/Pictures/wallpapers/5.png +$background = 1a110f +$foreground = f1dfd9 -$primary = afc6ff -$secondary = bfc6dc -$tertiary = dfbbde -$surface = 121318 -$surface_bright = 38393e -$outline = 8f9099 +$primary = ffb59c +$secondary = e7bdb0 +$tertiary = d6c68d +$surface = 1a110f +$surface_bright = 423733 +$outline = a08d87 $error = ff5945 $shadow = 000000 -$red = ffb2b9 -$green = 95d5a7 -$yellow = b8cf84 -$blue = b7c4ff -$magenta = e4b7f3 -$cyan = 82d3e2 +$red = ffb4a2 +$green = b7d085 +$yellow = dec56e +$blue = cebdfe +$magenta = fcb0d5 +$cyan = 84d5c4 $white = 82d3e0 -$red_dim = ff4c5c -$green_dim = 4cb86a -$yellow_dim = 8baa43 -$blue_dim = 5170ff -$magenta_dim = c360e4 -$cyan_dim = 30b5ce +$red_dim = ff623c +$green_dim = 89ad42 +$yellow_dim = bb9b2b +$blue_dim = 8359fc +$magenta_dim = f84ea1 +$cyan_dim = 3eb59c diff --git a/modules/dock/components/__init__.py b/modules/dock/components/__init__.py index 66ac421..7e59c7d 100644 --- a/modules/dock/components/__init__.py +++ b/modules/dock/components/__init__.py @@ -6,7 +6,6 @@ import os import gi from fabric.hyprland.service import HyprlandEvent from fabric.hyprland.widgets import Language, get_hyprland_connection -from fabric.system_tray.widgets import SystemTray from fabric.utils import get_relative_path from fabric.widgets.box import Box from fabric.widgets.button import Button @@ -23,59 +22,12 @@ from .indicators import Indicators from .metrics import Metrics from .music_player import MusicPlayer from .workspaces import workspace +from .systemtray import SystemTray gi.require_version("Gtk", "3.0") # Monkey patch SystemTray to use GTK theme icons -def _patched_do_update_properties(self, *_): - """Enhanced update method that prioritizes GTK theme icons""" - icon_name = self._item.icon_name - - if icon_name: - # Use the default GTK icon theme to load the icon - icon_theme = Gtk.IconTheme.get_default() - - try: - # Try to load the icon from the GTK theme first - if icon_theme.has_icon(icon_name): - self._image.set_from_icon_name(icon_name, self._icon_size) - else: - # Fallback to original behavior (pixbuf) - pixbuf = self._item.get_preferred_icon_pixbuf(self._icon_size) - if pixbuf is not None: - self._image.set_from_pixbuf(pixbuf) - else: - self._image.set_from_icon_name("image-missing", self._icon_size) - except Exception: - # Fallback to original behavior on any error - pixbuf = self._item.get_preferred_icon_pixbuf(self._icon_size) - if pixbuf is not None: - self._image.set_from_pixbuf(pixbuf) - else: - self._image.set_from_icon_name("image-missing", self._icon_size) - else: - # No icon name, use original behavior - pixbuf = self._item.get_preferred_icon_pixbuf(self._icon_size) - if pixbuf is not None: - self._image.set_from_pixbuf(pixbuf) - else: - self._image.set_from_icon_name("image-missing", self._icon_size) - - # Set tooltip (same as original) - tooltip = self._item.tooltip - self.set_tooltip_markup( - tooltip.description or tooltip.title or self._item.title.title() - if self._item.title - else "Unknown" - ) - return - - -# Apply the patch -SystemTrayItem.do_update_properties = _patched_do_update_properties - - class DockComponents(Box): def __init__(self, orientation_val="h", dock_instance=None, **kwargs): super().__init__( @@ -115,12 +67,7 @@ class DockComponents(Box): self.controls = Controls() self.indicators = Indicators() self.music_player = MusicPlayer() - self.systray = SystemTray( - icon_size=18, - spacing=4, - name="tray", - orientation="h" if not data.VERTICAL else "v", - ) + self.systray = SystemTray() # Create list of components to add self.themed_children = [ diff --git a/modules/dock/components/systemtray.py b/modules/dock/components/systemtray.py new file mode 100644 index 0000000..bdc3052 --- /dev/null +++ b/modules/dock/components/systemtray.py @@ -0,0 +1,170 @@ +import config.data as data +from gi.repository import Gdk, GdkPixbuf, GLib, Gray, Gtk +from fabric.widgets.box import Box +import logging +import gi + +gi.require_version("Gray", "0.1") + + +logger = logging.getLogger(__name__) + + +class SystemTray(Box): + def __init__( + self, pixel_size: int = 16, refresh_interval: int = 1, **kwargs + ) -> None: + orientation = ( + Gtk.Orientation.HORIZONTAL + if not data.VERTICAL + else Gtk.Orientation.VERTICAL + ) + super().__init__(name="tray", orientation=orientation, spacing=4, **kwargs) + self.enabled = True + super().set_visible(False) + self.pixel_size = pixel_size + self.refresh_interval = refresh_interval + + self.buttons_by_id = {} + self.items_by_id = {} + + self.watcher = Gray.Watcher() + self.watcher.connect("item-added", self.on_watcher_item_added) + + GLib.timeout_add_seconds(self.refresh_interval, self._refresh_all_items) + + def set_visible(self, visible: bool): + self.enabled = visible + self._update_visibility() + + def _update_visibility(self): + has = len(self.get_children()) > 0 + super().set_visible(self.enabled and has) + + def _get_item_pixbuf(self, item: Gray.Item) -> GdkPixbuf.Pixbuf: + try: + pm = Gray.get_pixmap_for_pixmaps(item.get_icon_pixmaps(), self.pixel_size) + if pm: + return pm.as_pixbuf(self.pixel_size, GdkPixbuf.InterpType.HYPER) + + name = item.get_icon_name() + theme = Gtk.IconTheme.get_default() + path = item.get_icon_theme_path() + if path: + theme.prepend_search_path(path) + return theme.load_icon( + name, self.pixel_size, Gtk.IconLookupFlags.FORCE_SIZE + ) + except GLib.Error as e: + logger.debug(f"Icon load error {e}") + return Gtk.IconTheme.get_default().load_icon( + "image-missing", self.pixel_size, Gtk.IconLookupFlags.FORCE_SIZE + ) + + def _refresh_item_ui(self, identifier: str, item: Gray.Item, button: Gtk.Button): + pixbuf = self._get_item_pixbuf(item) + img = button.get_image() + if isinstance(img, Gtk.Image): + img.set_from_pixbuf(pixbuf) + else: + new = Gtk.Image.new_from_pixbuf(pixbuf) + button.set_image(new) + new.show() + tip = None + if hasattr(item, "get_tooltip_text"): + tip = item.get_tooltip_text() + elif hasattr(item, "get_title"): + tip = item.get_title() + if tip: + button.set_tooltip_text(tip) + else: + button.set_has_tooltip(False) + + def _refresh_all_items(self) -> bool: + for ident, item in self.items_by_id.items(): + btn = self.buttons_by_id.get(ident) + if btn: + self._refresh_item_ui(ident, item, btn) + return True + + def on_watcher_item_added(self, _, identifier: str): + item = self.watcher.get_item_for_identifier(identifier) + if not item: + return + + if identifier in self.buttons_by_id: + self.buttons_by_id[identifier].destroy() + del self.buttons_by_id[identifier] + del self.items_by_id[identifier] + + btn = self.do_bake_item_button(item) + self.buttons_by_id[identifier] = btn + self.items_by_id[identifier] = item + + item.connect( + "notify::icon-pixmaps", + lambda itm, pspec: self._refresh_item_ui(identifier, itm, btn), + ) + item.connect( + "notify::icon-name", + lambda itm, pspec: self._refresh_item_ui(identifier, itm, btn), + ) + + try: + item.connect( + "updated", lambda itm: self._refresh_item_ui(identifier, itm, btn) + ) + except TypeError: + pass + + item.connect( + "removed", lambda itm: self.on_item_instance_removed(identifier, itm) + ) + + self.add(btn) + btn.show_all() + self._update_visibility() + + def do_bake_item_button(self, item: Gray.Item) -> Gtk.Button: + btn = Gtk.Button() + btn.connect("button-press-event", lambda b, e: self.on_button_click(b, item, e)) + img = Gtk.Image.new_from_pixbuf(self._get_item_pixbuf(item)) + btn.set_image(img) + tip = ( + item.get_tooltip_text() + if hasattr(item, "get_tooltip_text") + else getattr(item, "get_title", lambda: None)() + ) + if tip: + btn.set_tooltip_text(tip) + return btn + + def on_item_instance_removed(self, identifier: str, removed_item: Gray.Item): + if self.items_by_id.get(identifier) is removed_item: + btn = self.buttons_by_id.pop(identifier, None) + self.items_by_id.pop(identifier, None) + if btn: + btn.destroy() + self._update_visibility() + + def on_button_click( + self, button: Gtk.Button, item: Gray.Item, event: Gdk.EventButton + ): + if event.button == Gdk.BUTTON_PRIMARY: + try: + item.activate(int(event.x_root), int(event.y_root)) + except Exception as e: + logger.error(f"Activate error: {e}") + elif event.button == Gdk.BUTTON_SECONDARY: + menu = getattr(item, "get_menu", lambda: None)() + if isinstance(menu, Gtk.Menu): + menu.popup_at_widget( + button, Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.NORTH_WEST, event + ) + else: + cm = getattr(item, "context_menu", None) + if cm: + try: + cm(int(event.x_root), int(event.y_root)) + except Exception as e: + logger.error(f"ContextMenu error: {e}")