248 lines
9.1 KiB
Python
248 lines
9.1 KiB
Python
from gi.repository import Gtk
|
|
from fabric.system_tray.widgets import SystemTrayItem
|
|
import json
|
|
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
|
|
from fabric.widgets.datetime import DateTime
|
|
from fabric.widgets.label import Label
|
|
|
|
import config.data as data
|
|
import utils.icons as icons
|
|
|
|
from .applications import Applications
|
|
from .battery import Battery
|
|
from .controls import Controls
|
|
from .indicators import Indicators
|
|
from .metrics import Metrics
|
|
from .music_player import MusicPlayer
|
|
from .workspaces import workspace
|
|
|
|
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__(
|
|
name="dock-components", orientation=orientation_val, spacing=8, **kwargs
|
|
)
|
|
|
|
self.dock_instance = dock_instance
|
|
|
|
# Create applications with dock reference
|
|
self.applications = Applications(
|
|
orientation_val=orientation_val, dock_instance=dock_instance
|
|
)
|
|
|
|
self.connection = get_hyprland_connection()
|
|
self.lang_label = Label(name="lang-label")
|
|
self.language = Button(
|
|
name="language", h_align="center", v_align="center", child=self.lang_label
|
|
)
|
|
self.on_language_switch()
|
|
self.connection.connect("event::activelayout", self.on_language_switch)
|
|
|
|
# Initialize component visibility from data
|
|
self.component_visibility = data.DOCK_COMPONENTS_VISIBILITY
|
|
|
|
# Create components
|
|
self.workspaces = workspace
|
|
self.metrics = Metrics()
|
|
self.battery = Battery()
|
|
self.date_time = DateTime(
|
|
name="date-time",
|
|
formatters=["%I:%M"] if not data.VERTICAL else ["%I\n%M"],
|
|
h_align="center" if not data.VERTICAL else "fill",
|
|
v_align="center",
|
|
h_expand=True,
|
|
v_expand=True,
|
|
)
|
|
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",
|
|
)
|
|
|
|
# Create list of components to add
|
|
self.themed_children = [
|
|
self.workspaces,
|
|
self.metrics,
|
|
self.controls,
|
|
self.music_player,
|
|
self.applications,
|
|
self.indicators,
|
|
self.battery,
|
|
self.date_time,
|
|
self.language,
|
|
self.systray,
|
|
]
|
|
|
|
# Add each child individually
|
|
for child in self.themed_children:
|
|
self.add(child)
|
|
|
|
# Apply initial visibility
|
|
self.apply_component_props()
|
|
|
|
# Apply invert style for specific themes and positions
|
|
should_invert = data.DOCK_THEME in ["Dense", "Edge"] or (
|
|
data.DOCK_THEME == "Pills"
|
|
and data.DOCK_POSITION in ["Left", "Right", "Top"]
|
|
)
|
|
|
|
if should_invert:
|
|
for child in self.themed_children:
|
|
if hasattr(child, "add_style_class"):
|
|
child.add_style_class("invert")
|
|
|
|
def apply_component_props(self):
|
|
components = {
|
|
"workspace": self.workspaces,
|
|
"metrics": self.metrics,
|
|
"battery": self.battery,
|
|
"date_time": self.date_time,
|
|
"controls": self.controls,
|
|
"indicators": self.indicators,
|
|
"music_player": self.music_player,
|
|
"systray": self.systray,
|
|
"applications": self.applications,
|
|
"language": self.language,
|
|
}
|
|
|
|
for component_name, widget in components.items():
|
|
if component_name in self.component_visibility:
|
|
visibility = self.component_visibility[component_name]
|
|
# Special handling for applications component - let it manage its own visibility
|
|
if component_name == "applications":
|
|
# Only apply config visibility if the component has content
|
|
if hasattr(widget, "_update_visibility_based_on_content"):
|
|
widget._update_visibility_based_on_content()
|
|
else:
|
|
widget.set_visible(visibility)
|
|
# Special handling for battery - let it manage its own visibility based on config and battery state
|
|
elif component_name == "battery":
|
|
# Battery component manages its own visibility based on config and battery presence
|
|
# Don't override its visibility here, let it handle it internally
|
|
pass
|
|
# Special handling for music player - let it manage its own visibility based on media content
|
|
elif component_name == "music_player":
|
|
# Music player component manages its own visibility based on config and media presence
|
|
# Don't override its visibility here, let it handle it internally
|
|
pass
|
|
else:
|
|
widget.set_visible(visibility)
|
|
|
|
def toggle_component_visibility(self, component_name):
|
|
components = {
|
|
"workspace": self.workspaces,
|
|
"metrics": self.metrics,
|
|
"battery": self.battery,
|
|
"date_time": self.date_time,
|
|
"controls": self.controls,
|
|
"indicators": self.indicators,
|
|
"music_player": self.music_player,
|
|
"systray": self.systray,
|
|
"applications": self.applications,
|
|
"language": self.language,
|
|
}
|
|
|
|
if component_name in components and component_name in self.component_visibility:
|
|
self.component_visibility[component_name] = not self.component_visibility[
|
|
component_name
|
|
]
|
|
|
|
components[component_name].set_visible(
|
|
self.component_visibility[component_name]
|
|
)
|
|
|
|
config_file = get_relative_path("../../../config/assets/config.json")
|
|
if os.path.exists(config_file):
|
|
try:
|
|
with open(config_file, "r") as f:
|
|
config = json.load(f)
|
|
|
|
config[f"dock_{component_name}_visible"] = (
|
|
self.component_visibility[component_name]
|
|
)
|
|
|
|
with open(config_file, "w") as f:
|
|
json.dump(config, f, indent=4)
|
|
except Exception as e:
|
|
print(f"Error updating config file: {e}")
|
|
|
|
return self.component_visibility[component_name]
|
|
|
|
return None
|
|
|
|
def on_language_switch(self, _=None, event: HyprlandEvent = None):
|
|
lang_data = (
|
|
event.data[1]
|
|
if event and event.data and len(event.data) > 1
|
|
else Language().get_label()
|
|
)
|
|
self.language.set_tooltip_text(lang_data)
|
|
if not data.VERTICAL:
|
|
self.lang_label.set_label(lang_data[:2].lower())
|
|
else:
|
|
self.lang_label.add_style_class("icon")
|
|
self.lang_label.set_markup(icons.keyboard)
|