✨ feat: add on-screen display for volume and brightness
This commit is contained in:
1 parent
7dae966684
commit
4822693298
6 files changed
+663
-44
No files matched your search
@@ -4,6 +4,7 @@
|
||||
@import url("./styles/dock.css");
|
||||
@import url("./styles/switcher.css");
|
||||
@import url("./styles/launcher.css");
|
||||
@import url("./styles/osd.css");
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
|
||||
@@ -10,14 +10,14 @@ from modules.panel.main import Panel
|
||||
from modules.dock import Dock
|
||||
from modules.switcher import ApplicationSwitcher
|
||||
from modules.launcher.main import Launcher
|
||||
|
||||
from modules.osd import OSD
|
||||
|
||||
for log in [
|
||||
"fabric.hyprland.widgets",
|
||||
"fabric.audio.service",
|
||||
"fabric.bluetooth.service",
|
||||
"services.network",
|
||||
"utils.wayland"
|
||||
"utils.wayland",
|
||||
]:
|
||||
logger.disable(log)
|
||||
|
||||
@@ -36,6 +36,7 @@ if __name__ == "__main__":
|
||||
switcher = ApplicationSwitcher()
|
||||
launcher = Launcher()
|
||||
panel.launcher = launcher
|
||||
osd = OSD()
|
||||
|
||||
# Set corners visibility based on config
|
||||
# corners_visible = config.get("corners_visible", True)
|
||||
@@ -52,7 +53,7 @@ if __name__ == "__main__":
|
||||
_ = color_css_file.connect("changed", lambda *_: set_css())
|
||||
|
||||
# Make sure corners is added to the app
|
||||
app = Application(f"{APP_NAME}", panel, dock, switcher, launcher)
|
||||
app = Application(f"{APP_NAME}", panel, dock, switcher, launcher, osd)
|
||||
|
||||
def set_css():
|
||||
app.set_stylesheet_from_file(
|
||||
|
||||
+442
@@ -0,0 +1,442 @@
|
||||
import time
|
||||
from typing import ClassVar, Literal
|
||||
|
||||
from fabric.audio import Audio
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.revealer import Revealer
|
||||
from fabric.widgets.scale import Scale, ScaleMark
|
||||
from gi.repository import GLib, GObject
|
||||
|
||||
import utils.icons as icons
|
||||
from services.brightness import Brightness
|
||||
from utils.animator import Animator
|
||||
from utils.wayland import WaylandWindow as Window
|
||||
|
||||
|
||||
class AnimatedScale(Scale):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.animator = None
|
||||
|
||||
def animate_value(self, value: float):
|
||||
if not self.animator:
|
||||
self.animator = Animator(
|
||||
bezier_curve=(0.34, 1.56, 0.64, 1.0),
|
||||
duration=0.8,
|
||||
min_value=self.min_value,
|
||||
max_value=self.value,
|
||||
tick_widget=self,
|
||||
notify_value=lambda p, *_: self.set_value(p.value),
|
||||
)
|
||||
self.animator.pause()
|
||||
self.animator.min_value = self.value
|
||||
self.animator.max_value = value
|
||||
self.animator.play()
|
||||
|
||||
|
||||
class BrightnessOSDContainer(Box):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs, orientation="h", spacing=12, name="osd")
|
||||
self.brightness_service = Brightness.get_initial()
|
||||
self.icon = Label(markup=icons.brightness, name="brighntess-icons")
|
||||
self.scale = AnimatedScale(
|
||||
marks=(ScaleMark(value=i) for i in range(0, 101, 10)),
|
||||
value=70,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
increments=(1, 1),
|
||||
orientation="h",
|
||||
)
|
||||
|
||||
self.add(self.icon)
|
||||
self.add(self.scale)
|
||||
self.update_brightness()
|
||||
|
||||
self.scale.connect("value-changed", lambda *_: self.update_brightness())
|
||||
self.brightness_service.connect("screen", self.on_brightness_changed)
|
||||
|
||||
def update_brightness(self) -> None:
|
||||
current_brightness = self.brightness_service.screen_brightness
|
||||
normalized_brightness = self._normalize_brightness(current_brightness)
|
||||
if current_brightness != 0:
|
||||
self.scale.animate_value(normalized_brightness)
|
||||
self.update_icon(normalized_brightness)
|
||||
|
||||
def update_icon(self, brightness_percentage: float) -> None:
|
||||
icon_name = self.get_brightness_icon(brightness_percentage)
|
||||
self.icon.set_label(icon_name)
|
||||
|
||||
def on_brightness_changed(self, _sender: any, value: float, *_args) -> None:
|
||||
normalized_brightness = self._normalize_brightness(value)
|
||||
self.scale.animate_value(normalized_brightness)
|
||||
self.update_icon(normalized_brightness)
|
||||
|
||||
def _normalize_brightness(self, brightness: float) -> float:
|
||||
return (brightness / self.brightness_service.max_screen) * 100
|
||||
|
||||
def get_brightness_icon(self, brightness_percentage: float) -> str:
|
||||
if brightness_percentage >= 67:
|
||||
return icons.brightness_high
|
||||
elif brightness_percentage >= 34:
|
||||
return icons.brightness_medium
|
||||
else:
|
||||
return icons.brightness_low
|
||||
|
||||
|
||||
class AudioOSDContainer(Box):
|
||||
__gsignals__: ClassVar[dict] = {
|
||||
"volume-changed": (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs, orientation="h", spacing=13, name="osd")
|
||||
self.audio = Audio()
|
||||
self.icon = Label(markup=icons.vol_high, name="vol-icon")
|
||||
self.scale = AnimatedScale(
|
||||
marks=(ScaleMark(value=i) for i in range(1, 100, 10)),
|
||||
value=70,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
increments=(1, 1),
|
||||
orientation="h",
|
||||
)
|
||||
|
||||
self.previous_volume = None
|
||||
self.previous_muted = None
|
||||
|
||||
self.add(self.icon)
|
||||
self.add(self.scale)
|
||||
self.sync_with_audio()
|
||||
|
||||
self.scale.connect("value-changed", self.on_volume_changed)
|
||||
self.audio.connect("notify::speaker", self.on_audio_speaker_changed)
|
||||
|
||||
# Connect to speaker-changed signal directly
|
||||
self.audio.connect("speaker-changed", self.on_speaker_changed)
|
||||
|
||||
# Connect to current speaker if available
|
||||
if self.audio.speaker:
|
||||
self.audio.speaker.connect(
|
||||
"notify::volume", self.on_volume_property_changed
|
||||
)
|
||||
self.audio.speaker.connect("notify::muted", self.on_mute_changed)
|
||||
|
||||
def sync_with_audio(self):
|
||||
if self.audio.speaker:
|
||||
volume = round(self.audio.speaker.volume)
|
||||
self.scale.set_value(volume)
|
||||
self.update_icon(volume)
|
||||
self.previous_volume = volume
|
||||
self.previous_muted = self.audio.speaker.muted
|
||||
|
||||
def on_volume_changed(self, *_):
|
||||
if self.audio.speaker:
|
||||
volume = self.scale.value
|
||||
if 0 <= volume <= 100:
|
||||
self.audio.speaker.set_volume(volume)
|
||||
self.update_icon(volume)
|
||||
|
||||
if volume == 0 or (self.audio.speaker and self.audio.speaker.muted):
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
self.icon.remove_style_class("muted")
|
||||
|
||||
self.emit("volume-changed")
|
||||
|
||||
def on_audio_speaker_changed(self, *_):
|
||||
if self.audio.speaker:
|
||||
self.audio.speaker.connect(
|
||||
"notify::volume", self.on_volume_property_changed
|
||||
)
|
||||
self.audio.speaker.connect("notify::muted", self.on_mute_changed)
|
||||
self.update_volume()
|
||||
|
||||
def on_speaker_changed(self, *_):
|
||||
self.update_volume()
|
||||
self.update_icon(round(self.audio.speaker.volume))
|
||||
|
||||
def on_volume_property_changed(self, *_):
|
||||
if self.audio.speaker:
|
||||
current_volume = round(self.audio.speaker.volume)
|
||||
if self.previous_volume is None or current_volume != self.previous_volume:
|
||||
self.previous_volume = current_volume
|
||||
self.update_volume()
|
||||
self.update_icon(current_volume)
|
||||
self.emit("volume-changed")
|
||||
|
||||
def update_volume(self, *_):
|
||||
if self.audio.speaker and not self.is_hovered():
|
||||
volume = round(self.audio.speaker.volume)
|
||||
self.scale.set_value(volume)
|
||||
self.update_icon(volume)
|
||||
|
||||
if volume == 0 or (self.audio.speaker and self.audio.speaker.muted):
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
self.icon.remove_style_class("muted")
|
||||
|
||||
def update_icon(self, volume):
|
||||
if not self.audio.speaker:
|
||||
return
|
||||
|
||||
vol_high_icon = icons.vol_high
|
||||
vol_medium_icon = icons.vol_medium
|
||||
vol_mute_icon = icons.vol_off
|
||||
vol_off_icon = icons.vol_mute
|
||||
|
||||
if self.audio.speaker and "bluetooth" in self.audio.speaker.icon_name:
|
||||
vol_high_icon = icons.bluetooth_connected
|
||||
vol_medium_icon = icons.bluetooth
|
||||
vol_mute_icon = icons.bluetooth_off
|
||||
vol_off_icon = icons.bluetooth_disconnected
|
||||
|
||||
if self.audio.speaker.muted:
|
||||
self.icon.set_label(vol_mute_icon)
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
if volume == 0:
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
self.icon.remove_style_class("muted")
|
||||
|
||||
if volume >= 67:
|
||||
self.icon.set_label(vol_high_icon)
|
||||
elif volume >= 34:
|
||||
self.icon.set_label(vol_medium_icon)
|
||||
elif volume >= 1:
|
||||
# Use medium for low volumes too
|
||||
self.icon.set_label(vol_medium_icon)
|
||||
else:
|
||||
self.icon.set_label(vol_off_icon)
|
||||
|
||||
def _get_audio_icon_name(self, volume: int) -> str:
|
||||
if volume >= 67:
|
||||
return icons.vol_high
|
||||
elif volume >= 34:
|
||||
return icons.vol_medium
|
||||
elif volume >= 1:
|
||||
return icons.vol_mute
|
||||
else:
|
||||
return icons.vol_off
|
||||
|
||||
def on_mute_changed(self, *_):
|
||||
if self.audio.speaker:
|
||||
current_muted = self.audio.speaker.muted
|
||||
if self.previous_muted is None or current_muted != self.previous_muted:
|
||||
self.previous_muted = current_muted
|
||||
self.update_icon(round(self.audio.speaker.volume))
|
||||
GLib.idle_add(lambda: self.emit("volume-changed"))
|
||||
|
||||
|
||||
class MicrophoneOSDContainer(Box):
|
||||
__gsignals__: ClassVar[dict] = {
|
||||
"mic-changed": (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs, orientation="h", spacing=13, name="osd")
|
||||
self.audio = Audio()
|
||||
self.icon = Label(markup=icons.mic, name="mic-icon")
|
||||
self.scale = AnimatedScale(
|
||||
marks=(ScaleMark(value=i) for i in range(1, 100, 10)),
|
||||
value=70,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
increments=(1, 1),
|
||||
orientation="h",
|
||||
)
|
||||
|
||||
self.previous_volume = None
|
||||
self.previous_muted = None
|
||||
|
||||
self.add(self.icon)
|
||||
self.add(self.scale)
|
||||
self.sync_with_audio()
|
||||
|
||||
self.scale.connect("value-changed", self.on_volume_changed)
|
||||
self.audio.connect("notify::microphone", self.on_audio_microphone_changed)
|
||||
|
||||
# Connect to microphone-changed signal directly
|
||||
self.audio.connect("microphone-changed", self.on_microphone_changed)
|
||||
|
||||
if self.audio.microphone:
|
||||
self.audio.microphone.connect(
|
||||
"notify::volume", self.on_volume_property_changed
|
||||
)
|
||||
self.audio.microphone.connect("notify::muted", self.on_mute_changed)
|
||||
|
||||
def sync_with_audio(self):
|
||||
if self.audio.microphone:
|
||||
volume = round(self.audio.microphone.volume)
|
||||
self.scale.set_value(volume)
|
||||
self.update_icon(volume)
|
||||
self.previous_volume = volume
|
||||
self.previous_muted = self.audio.microphone.muted
|
||||
|
||||
def on_volume_changed(self, *_):
|
||||
if self.audio.microphone:
|
||||
volume = self.scale.value
|
||||
if 0 <= volume <= 100:
|
||||
self.audio.microphone.set_volume(volume)
|
||||
self.update_icon(volume)
|
||||
|
||||
if volume == 0 or (
|
||||
self.audio.microphone and self.audio.microphone.muted
|
||||
):
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
self.icon.remove_style_class("muted")
|
||||
|
||||
self.emit("mic-changed")
|
||||
|
||||
def on_audio_microphone_changed(self, *_):
|
||||
if self.audio.microphone:
|
||||
self.audio.microphone.connect(
|
||||
"notify::volume", self.on_volume_property_changed
|
||||
)
|
||||
self.audio.microphone.connect("notify::muted", self.on_mute_changed)
|
||||
self.update_volume()
|
||||
|
||||
def on_microphone_changed(self, *_):
|
||||
self.update_volume()
|
||||
self.update_icon(round(self.audio.microphone.volume))
|
||||
|
||||
def on_volume_property_changed(self, *_):
|
||||
if self.audio.microphone:
|
||||
current_volume = round(self.audio.microphone.volume)
|
||||
if self.previous_volume is None or current_volume != self.previous_volume:
|
||||
self.previous_volume = current_volume
|
||||
self.update_volume()
|
||||
self.update_icon(current_volume)
|
||||
self.emit("mic-changed")
|
||||
|
||||
def update_volume(self, *_):
|
||||
if self.audio.microphone and not self.is_hovered():
|
||||
volume = round(self.audio.microphone.volume)
|
||||
self.scale.set_value(volume)
|
||||
self.update_icon(volume)
|
||||
|
||||
if volume == 0 or (self.audio.microphone and self.audio.microphone.muted):
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
self.icon.remove_style_class("muted")
|
||||
|
||||
def update_icon(self, volume):
|
||||
if not self.audio.microphone:
|
||||
return
|
||||
|
||||
if self.audio.microphone.muted:
|
||||
self.icon.set_label(icons.mic_mute)
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
if volume == 0:
|
||||
self.scale.add_style_class("muted")
|
||||
self.icon.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
self.icon.remove_style_class("muted")
|
||||
|
||||
if volume >= 67:
|
||||
self.icon.set_label(icons.mic)
|
||||
elif volume >= 34:
|
||||
self.icon.set_label(icons.mic)
|
||||
elif volume >= 1:
|
||||
self.icon.set_label(icons.mic)
|
||||
else:
|
||||
self.icon.set_label(icons.mic_mute)
|
||||
|
||||
def on_mute_changed(self, *_):
|
||||
if self.audio.microphone:
|
||||
current_muted = self.audio.microphone.muted
|
||||
if self.previous_muted is None or current_muted != self.previous_muted:
|
||||
self.previous_muted = current_muted
|
||||
self.update_icon(round(self.audio.microphone.volume))
|
||||
GLib.idle_add(lambda: self.emit("mic-changed"))
|
||||
|
||||
|
||||
class OSD(Window):
|
||||
def __init__(self, **kwargs):
|
||||
self.audio_container = AudioOSDContainer()
|
||||
self.brightness_container = BrightnessOSDContainer()
|
||||
self.microphone_container = MicrophoneOSDContainer()
|
||||
|
||||
self.timeout = 1000
|
||||
|
||||
self.revealer = Revealer(
|
||||
transition_type="slide-up",
|
||||
transition_duration=100,
|
||||
child_revealed=False,
|
||||
)
|
||||
|
||||
self.main_box = Box(
|
||||
orientation="v",
|
||||
spacing=13,
|
||||
children=[self.revealer],
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
layer="overlay",
|
||||
anchor="bottom",
|
||||
child=self.main_box,
|
||||
visible=False,
|
||||
pass_through=True,
|
||||
keyboard_mode="on-demand",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.last_activity_time = time.time()
|
||||
|
||||
self.audio_container.connect("volume-changed", self.show_audio)
|
||||
self.brightness_container.brightness_service.connect(
|
||||
"screen", self.show_brightness
|
||||
)
|
||||
self.microphone_container.connect("mic-changed", self.show_microphone)
|
||||
|
||||
GLib.timeout_add(100, self.check_inactivity)
|
||||
|
||||
def show_audio(self, *_):
|
||||
self.show_box(box_to_show="audio")
|
||||
self.reset_inactivity_timer()
|
||||
|
||||
def show_brightness(self, *_):
|
||||
self.show_box(box_to_show="brightness")
|
||||
self.reset_inactivity_timer()
|
||||
|
||||
def show_microphone(self, *_):
|
||||
self.show_box(box_to_show="microphone")
|
||||
self.reset_inactivity_timer()
|
||||
|
||||
def show_box(self, box_to_show: Literal["audio", "brightness", "microphone"]):
|
||||
self.set_visible(True)
|
||||
if box_to_show == "audio":
|
||||
self.revealer.children = self.audio_container
|
||||
elif box_to_show == "brightness":
|
||||
self.revealer.children = self.brightness_container
|
||||
elif box_to_show == "microphone":
|
||||
self.revealer.children = self.microphone_container
|
||||
self.revealer.set_reveal_child(True)
|
||||
self.reset_inactivity_timer()
|
||||
|
||||
def start_hide_timer(self):
|
||||
self.set_visible(False)
|
||||
|
||||
def reset_inactivity_timer(self):
|
||||
self.last_activity_time = time.time()
|
||||
|
||||
def check_inactivity(self):
|
||||
if time.time() - self.last_activity_time >= (self.timeout / 1000):
|
||||
self.start_hide_timer()
|
||||
return True
|
||||
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
Enhanced System Tray Icon Handling
|
||||
|
||||
This module provides enhanced icon loading capabilities for system tray items,
|
||||
including fallback mechanisms for file paths and common icon locations.
|
||||
"""
|
||||
|
||||
import os
|
||||
from gi.repository import Gtk, GdkPixbuf
|
||||
from fabric.system_tray.widgets import SystemTrayItem
|
||||
|
||||
|
||||
def patched_do_update_properties(self, *_):
|
||||
# Try default GTK theme first
|
||||
icon_name = self._item.icon_name
|
||||
attention_icon_name = self._item.attention_icon_name
|
||||
|
||||
if self._item.status == "NeedsAttention" and attention_icon_name:
|
||||
preferred_icon_name = attention_icon_name
|
||||
else:
|
||||
preferred_icon_name = icon_name
|
||||
|
||||
# Try to load from default GTK theme
|
||||
if preferred_icon_name:
|
||||
try:
|
||||
default_theme = Gtk.IconTheme.get_default()
|
||||
if default_theme.has_icon(preferred_icon_name):
|
||||
pixbuf = default_theme.load_icon(
|
||||
preferred_icon_name, self._icon_size, Gtk.IconLookupFlags.FORCE_SIZE
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
# Set tooltip
|
||||
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
|
||||
except:
|
||||
pass
|
||||
|
||||
# Enhanced fallback handling for file paths
|
||||
if preferred_icon_name and self._try_load_icon_from_path(preferred_icon_name):
|
||||
return
|
||||
|
||||
# Fallback to original implementation
|
||||
original_do_update_properties(self, *_)
|
||||
|
||||
|
||||
def _try_load_icon_from_path(self, icon_path):
|
||||
try:
|
||||
# Check if it's a file path and handle it directly
|
||||
if os.path.isabs(icon_path) or '/' in icon_path:
|
||||
# Try to load as SVG from the original path if it exists
|
||||
if os.path.exists(icon_path):
|
||||
if icon_path.lower().endswith('.svg'):
|
||||
# Load SVG directly
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
|
||||
icon_path, self._icon_size, self._icon_size
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
self._set_tooltip()
|
||||
return True
|
||||
else:
|
||||
# Load other image formats
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
|
||||
icon_path, self._icon_size, self._icon_size
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
self._set_tooltip()
|
||||
return True
|
||||
|
||||
# If it's a file path, try to extract just the filename for theme lookup
|
||||
filename = os.path.basename(icon_path)
|
||||
if filename:
|
||||
# Remove extension for theme lookup
|
||||
name_without_ext = os.path.splitext(filename)[0]
|
||||
default_theme = Gtk.IconTheme.get_default()
|
||||
|
||||
# Try filename without extension
|
||||
if default_theme.has_icon(name_without_ext):
|
||||
pixbuf = default_theme.load_icon(
|
||||
name_without_ext, self._icon_size, Gtk.IconLookupFlags.FORCE_SIZE
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
self._set_tooltip()
|
||||
return True
|
||||
|
||||
# Try full filename
|
||||
if default_theme.has_icon(filename):
|
||||
pixbuf = default_theme.load_icon(
|
||||
filename, self._icon_size, Gtk.IconLookupFlags.FORCE_SIZE
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
self._set_tooltip()
|
||||
return True
|
||||
|
||||
# If it looks like a file path but doesn't exist, try common icon locations
|
||||
if os.path.isabs(icon_path):
|
||||
common_icon_dirs = [
|
||||
"/usr/share/icons",
|
||||
"/usr/share/pixmaps",
|
||||
"/usr/local/share/icons",
|
||||
"/usr/local/share/pixmaps",
|
||||
os.path.expanduser("~/.local/share/icons"),
|
||||
os.path.expanduser("~/.icons")
|
||||
]
|
||||
|
||||
filename = os.path.basename(icon_path)
|
||||
for icon_dir in common_icon_dirs:
|
||||
potential_path = os.path.join(icon_dir, filename)
|
||||
if os.path.exists(potential_path):
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
|
||||
potential_path, self._icon_size, self._icon_size
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
self._set_tooltip()
|
||||
return True
|
||||
except:
|
||||
continue
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _set_tooltip(self):
|
||||
tooltip = self._item.tooltip
|
||||
self.set_tooltip_markup(
|
||||
tooltip.description or tooltip.title or self._item.title.title()
|
||||
if self._item.title
|
||||
else "Unknown"
|
||||
)
|
||||
|
||||
|
||||
def apply_enhanced_system_tray():
|
||||
# Store original method
|
||||
global original_do_update_properties
|
||||
original_do_update_properties = SystemTrayItem.do_update_properties
|
||||
|
||||
# Attach helper methods to SystemTrayItem class
|
||||
SystemTrayItem._try_load_icon_from_path = _try_load_icon_from_path
|
||||
SystemTrayItem._set_tooltip = _set_tooltip
|
||||
|
||||
# Replace the do_update_properties method
|
||||
SystemTrayItem.do_update_properties = patched_do_update_properties
|
||||
|
||||
|
||||
# Store reference to original method
|
||||
original_do_update_properties = None
|
||||
+6
-41
@@ -1,54 +1,19 @@
|
||||
from fabric.system_tray.widgets import SystemTray, SystemTrayItem
|
||||
from fabric.utils import exec_shell_command, get_relative_path
|
||||
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.centerbox import CenterBox
|
||||
from fabric.widgets.datetime import DateTime
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.revealer import Revealer
|
||||
from gi.repository import Gtk
|
||||
from fabric.widgets.svg import Svg
|
||||
from modules.panel.components.indicators import Indicators
|
||||
from modules.panel.components.menubar import MenuBar
|
||||
from modules.panel.components.enhanced_system_tray import apply_enhanced_system_tray
|
||||
from utils.wayland import WaylandWindow as Window
|
||||
|
||||
original_do_update_properties = SystemTrayItem.do_update_properties
|
||||
|
||||
|
||||
def patched_do_update_properties(self, *_):
|
||||
# Try default GTK theme first
|
||||
icon_name = self._item.icon_name
|
||||
attention_icon_name = self._item.attention_icon_name
|
||||
|
||||
if self._item.status == "NeedsAttention" and attention_icon_name:
|
||||
preferred_icon_name = attention_icon_name
|
||||
else:
|
||||
preferred_icon_name = icon_name
|
||||
|
||||
# Try to load from default GTK theme
|
||||
if preferred_icon_name:
|
||||
try:
|
||||
default_theme = Gtk.IconTheme.get_default()
|
||||
if default_theme.has_icon(preferred_icon_name):
|
||||
pixbuf = default_theme.load_icon(
|
||||
preferred_icon_name, self._icon_size, Gtk.IconLookupFlags.FORCE_SIZE
|
||||
)
|
||||
if pixbuf:
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
# Set tooltip
|
||||
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
|
||||
except:
|
||||
pass
|
||||
original_do_update_properties(self, *_)
|
||||
|
||||
|
||||
SystemTrayItem.do_update_properties = patched_do_update_properties
|
||||
# Apply enhanced system tray icon handling
|
||||
apply_enhanced_system_tray()
|
||||
|
||||
|
||||
class Panel(Window):
|
||||
@@ -166,4 +131,4 @@ class Panel(Window):
|
||||
else:
|
||||
self.chevron_button.get_child().set_from_file(
|
||||
get_relative_path("../../config/assets/icons/chevron-right.svg")
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
#osd {
|
||||
background: var(--shadow);
|
||||
padding: 12px 20px;
|
||||
margin: 70px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
#osd scale {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
#osd trough {
|
||||
background: var(--surface);
|
||||
min-height: 10px;
|
||||
margin-right: 4px;
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
#osd trough highlight {
|
||||
border-radius: 100px 0px 0px 100px;
|
||||
background: var(--primary);
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
#osd.muted trough highlight,
|
||||
#osd.muted slider,
|
||||
#osd scale.muted trough highlight,
|
||||
#osd scale.muted slider {
|
||||
background-color: var(--surface-bright);
|
||||
}
|
||||
|
||||
#osd slider {
|
||||
background-color: var(--primary);
|
||||
background-size: 20px 20px;
|
||||
min-width: 4px;
|
||||
min-height: 8px;
|
||||
border-radius: 2px;
|
||||
padding: 0px;
|
||||
margin: -14px 0;
|
||||
box-shadow:
|
||||
-4px 0 0px 2px var(--shadow),
|
||||
4px 0 0px 2px var(--shadow);
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
#brighntess-icons.muted,
|
||||
#vol-icon.muted,
|
||||
#mic-icon.muted {
|
||||
color: var(--outline);
|
||||
}
|
||||
Reference in new issue
Block a user