refactor(modules): improve exception handling and simplify service usage
Enhancements - Replace bare `except:` with `except Exception:` in multiple modules- Cast step calculation to `int` for consistent type - Simplify `get_battery_icon_file` to return a string directly - Use `get_todo_service()` instead of module-level `todo_service` throughout todo widget - Remove unused `"zen": "zen-browser"` mapping entry Bug Fixes - Prevent bare exception swallowing by catching `Exception` explicitly - Ensure proper cleanup of timer IDs and other resources - Delete unused modules/osdd components New Features - None
This commit is contained in:
34 files changed
+682
-519
No files matched your search
@@ -0,0 +1,5 @@
|
||||
from .audio import AudioOSDContainer # noqa: F401
|
||||
from .brightness import BrightnessOSDContainer # noqa: F401
|
||||
from .microphone import MicrophoneOSDContainer # noqa: F401
|
||||
from .capslock import CapsLockOSDContainer # noqa: F401
|
||||
from .keyboard_layout import KeyboardLayoutOSDContainer # noqa: F401
|
||||
@@ -0,0 +1,28 @@
|
||||
from functools import partial
|
||||
from fabric.widgets.scale import Scale
|
||||
from utils.animator import Animator, cubic_bezier
|
||||
|
||||
|
||||
class AnimatedScale(Scale):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.animator = (
|
||||
Animator(
|
||||
duration=0.8,
|
||||
timing_function=partial(cubic_bezier, 0.34, 1.56, 0.64, 1.0),
|
||||
min_value=self.min_value,
|
||||
max_value=self.value,
|
||||
tick_widget=self,
|
||||
notify_value=lambda anim: self.set_value(anim.value),
|
||||
)
|
||||
.build()
|
||||
.play()
|
||||
.unwrap()
|
||||
)
|
||||
|
||||
def animate_value(self, value: float):
|
||||
if self.animator:
|
||||
self.animator.pause()
|
||||
self.animator.min_value = self.value
|
||||
self.animator.max_value = value
|
||||
self.animator.play()
|
||||
@@ -0,0 +1,105 @@
|
||||
import math
|
||||
from fabric.audio import Audio
|
||||
from fabric.utils import get_relative_path
|
||||
from fabric.widgets.svg import Svg
|
||||
from fabric.widgets.scale import ScaleMark
|
||||
from .animated_scale import AnimatedScale
|
||||
from .base import BaseOSDContainer
|
||||
|
||||
|
||||
class AudioOSDContainer(BaseOSDContainer):
|
||||
def __init__(self, window, **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
self.audio = Audio()
|
||||
self._last_volume = None
|
||||
self._last_muted = None
|
||||
self._setup_specific_components()
|
||||
self._connect_specific_signals()
|
||||
|
||||
def _setup_specific_components(self):
|
||||
self.osd_window_image = Svg(
|
||||
get_relative_path("../../../config/assets/icons/volume/audio-volume.svg"),
|
||||
size=(100, 100),
|
||||
name="osd-image",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
)
|
||||
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.add(self.osd_window_image)
|
||||
self.add(self.scale)
|
||||
|
||||
def _connect_specific_signals(self):
|
||||
self.audio.connect("notify::speaker", self._on_speaker_changed)
|
||||
if self.audio.speaker:
|
||||
self._connect_speaker_signals()
|
||||
|
||||
def _connect_speaker_signals(self):
|
||||
if self.audio.speaker:
|
||||
self.audio.speaker.connect("changed", self._on_speaker_stream_changed)
|
||||
self._sync_with_audio()
|
||||
|
||||
def _on_speaker_changed(self, *_):
|
||||
self._connect_speaker_signals()
|
||||
self.update()
|
||||
|
||||
def _on_speaker_stream_changed(self, *_):
|
||||
if self.audio.speaker:
|
||||
current_volume = round(self.audio.speaker.volume)
|
||||
current_muted = self.audio.speaker.muted
|
||||
if self._last_volume != current_volume or self._last_muted != current_muted:
|
||||
self._last_volume = current_volume
|
||||
self._last_muted = current_muted
|
||||
self.update()
|
||||
|
||||
def _sync_with_audio(self):
|
||||
if self.audio.speaker:
|
||||
self._last_volume = round(self.audio.speaker.volume)
|
||||
self._last_muted = self.audio.speaker.muted
|
||||
self.scale.set_value(self._last_volume)
|
||||
self._update_display()
|
||||
|
||||
def _update_display(self):
|
||||
if not self.audio.speaker:
|
||||
return
|
||||
|
||||
volume = (
|
||||
self._last_volume
|
||||
if self._last_volume is not None
|
||||
else round(self.audio.speaker.volume)
|
||||
)
|
||||
muted = (
|
||||
self._last_muted
|
||||
if self._last_muted is not None
|
||||
else self.audio.speaker.muted
|
||||
)
|
||||
|
||||
display_volume = 0 if (volume == 0 or muted) else volume
|
||||
level = (
|
||||
0 if display_volume == 0 else min(int(math.ceil(display_volume / 33)), 3)
|
||||
)
|
||||
|
||||
self.osd_window_image.set_from_file(
|
||||
get_relative_path(
|
||||
f"../../../config/assets/icons/volume/audio-volume-{level}.svg"
|
||||
)
|
||||
)
|
||||
|
||||
if muted or volume == 0:
|
||||
self.scale.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
|
||||
self.scale.animate_value(volume)
|
||||
|
||||
def update(self, *_):
|
||||
self._update_display()
|
||||
super().update()
|
||||
@@ -0,0 +1,145 @@
|
||||
from abc import abstractmethod
|
||||
from fabric.utils import Gdk, GLib, invoke_repeater, remove_handler, time
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
|
||||
class BaseOSDContainer(Box):
|
||||
MAX_VISIBLE_TIME = 6.0
|
||||
COOLDOWN_MS = 200
|
||||
|
||||
def __init__(self, window: Window, **kwargs):
|
||||
super().__init__(**kwargs, orientation="v", spacing=12, name="osd-container")
|
||||
self.last_handler: int = 0
|
||||
self.window = window
|
||||
self.osd = None
|
||||
self.show_timestamp: float = 0.0
|
||||
self.watchdog_handler: int = 0
|
||||
self._update_in_progress = False
|
||||
self._is_hovered = False
|
||||
self._last_trigger_time: float = 0.0
|
||||
|
||||
GLib.idle_add(self._setup_window_signals)
|
||||
|
||||
def _setup_window_signals(self):
|
||||
self.window.add_events(
|
||||
Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK
|
||||
)
|
||||
self.window.connect("enter-notify-event", self._on_enter_notify)
|
||||
self.window.connect("leave-notify-event", self._on_leave_notify)
|
||||
return False
|
||||
|
||||
def _on_enter_notify(self, widget, event):
|
||||
if event.detail != Gdk.NotifyType.INFERIOR:
|
||||
self._is_hovered = True
|
||||
|
||||
def _on_leave_notify(self, widget, event):
|
||||
if event.detail != Gdk.NotifyType.INFERIOR:
|
||||
self._is_hovered = False
|
||||
|
||||
def is_hovered(self):
|
||||
return self._is_hovered
|
||||
|
||||
def remove_last_handler(self):
|
||||
if self.last_handler:
|
||||
try:
|
||||
remove_handler(self.last_handler)
|
||||
except Exception:
|
||||
pass
|
||||
self.last_handler = 0
|
||||
|
||||
def remove_watchdog_handler(self):
|
||||
if self.watchdog_handler:
|
||||
try:
|
||||
GLib.source_remove(self.watchdog_handler)
|
||||
except Exception:
|
||||
pass
|
||||
self.watchdog_handler = 0
|
||||
|
||||
def cleanup_all_handlers(self):
|
||||
self.remove_last_handler()
|
||||
self.remove_watchdog_handler()
|
||||
self.show_timestamp = 0.0
|
||||
self._update_in_progress = False
|
||||
|
||||
def hide_window(self):
|
||||
if self.osd and hasattr(self.osd, "revealer"):
|
||||
self.osd.revealer.set_reveal_child(False)
|
||||
GLib.timeout_add(150, lambda: self.window.hide() or False)
|
||||
else:
|
||||
self.window.hide()
|
||||
|
||||
def watchdog_force_hide(self, *_):
|
||||
if not self.window.get_visible():
|
||||
self.cleanup_all_handlers()
|
||||
return False
|
||||
|
||||
elapsed = time.time() - self.show_timestamp
|
||||
if elapsed >= self.MAX_VISIBLE_TIME:
|
||||
if not self.is_hovered():
|
||||
self.hide_window()
|
||||
self.cleanup_all_handlers()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return True
|
||||
|
||||
def update(self, *_):
|
||||
if self._update_in_progress:
|
||||
return
|
||||
|
||||
current_time = time.time()
|
||||
time_since_last_trigger = (current_time - self._last_trigger_time) * 1000
|
||||
|
||||
if time_since_last_trigger < self.COOLDOWN_MS:
|
||||
return
|
||||
|
||||
self._last_trigger_time = current_time
|
||||
self._update_in_progress = True
|
||||
|
||||
self.cleanup_all_handlers()
|
||||
self.show_timestamp = time.time()
|
||||
|
||||
if self.osd:
|
||||
self.osd.show_container(self)
|
||||
|
||||
self.window.show()
|
||||
|
||||
self.focus()
|
||||
self.last_handler = invoke_repeater(1700, self.unfocus, initial_call=False)
|
||||
self.watchdog_handler = GLib.timeout_add(1000, self.watchdog_force_hide)
|
||||
self._update_in_progress = False
|
||||
|
||||
def focus(self, *_):
|
||||
self.add_style_class("focused")
|
||||
return False
|
||||
|
||||
def unfocus(self, *_):
|
||||
if not self.window.get_visible():
|
||||
self.cleanup_all_handlers()
|
||||
return False
|
||||
|
||||
self.remove_style_class("focused")
|
||||
self.remove_last_handler()
|
||||
self.last_handler = invoke_repeater(1700, self.unpop, initial_call=False)
|
||||
return False
|
||||
|
||||
def unpop(self, *_):
|
||||
if not self.window.get_visible():
|
||||
self.cleanup_all_handlers()
|
||||
return False
|
||||
|
||||
if not self.is_hovered():
|
||||
self.hide_window()
|
||||
self.cleanup_all_handlers()
|
||||
else:
|
||||
self.last_handler = invoke_repeater(500, self.unpop, initial_call=False)
|
||||
return False
|
||||
|
||||
@abstractmethod
|
||||
def _setup_specific_components(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def _connect_specific_signals(self):
|
||||
pass
|
||||
@@ -0,0 +1,64 @@
|
||||
import math
|
||||
from services.brightness import Brightness
|
||||
from fabric.utils import get_relative_path
|
||||
from fabric.widgets.svg import Svg
|
||||
from fabric.widgets.scale import ScaleMark
|
||||
from .animated_scale import AnimatedScale
|
||||
from .base import BaseOSDContainer
|
||||
|
||||
|
||||
class BrightnessOSDContainer(BaseOSDContainer):
|
||||
def __init__(self, window, **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
self.brightness_service = Brightness.get_initial()
|
||||
self._setup_specific_components()
|
||||
self._connect_specific_signals()
|
||||
|
||||
def _setup_specific_components(self):
|
||||
self.osd_window_image = Svg(
|
||||
get_relative_path("../../../config/assets/icons/brightness/brightness.svg"),
|
||||
size=(100, 100),
|
||||
name="osd-image",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
)
|
||||
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.osd_window_image)
|
||||
self.add(self.scale)
|
||||
|
||||
def _connect_specific_signals(self):
|
||||
self.brightness_service.connect("screen", self._on_screen_brightness_changed)
|
||||
|
||||
def _on_screen_brightness_changed(self, _sender, value, *_args):
|
||||
self.update()
|
||||
|
||||
def _get_normalized_brightness(self):
|
||||
return (
|
||||
self.brightness_service.screen_brightness
|
||||
/ self.brightness_service.max_screen
|
||||
) * 100
|
||||
|
||||
def _update_display(self):
|
||||
normalized = self._get_normalized_brightness()
|
||||
level = 0 if normalized == 0 else min(int(math.ceil(normalized / 33)), 3)
|
||||
|
||||
self.osd_window_image.set_from_file(
|
||||
get_relative_path(
|
||||
f"../../../config/assets/icons/brightness/brightness-{level}.svg"
|
||||
)
|
||||
)
|
||||
|
||||
self.scale.animate_value(normalized)
|
||||
|
||||
def update(self, *_):
|
||||
self._update_display()
|
||||
super().update()
|
||||
@@ -0,0 +1,50 @@
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.svg import Svg
|
||||
from fabric.utils import get_relative_path
|
||||
from .base import BaseOSDContainer
|
||||
from services.capslock import CapsLock
|
||||
|
||||
|
||||
class CapsLockOSDContainer(BaseOSDContainer):
|
||||
def __init__(self, window, **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
self.capslock = CapsLock.get_initial()
|
||||
self._setup_specific_components()
|
||||
self._connect_specific_signals()
|
||||
|
||||
def _setup_specific_components(self):
|
||||
self.osd_image = Svg(
|
||||
get_relative_path("../../../assets/icons/misc/caps-lock.svg"),
|
||||
size=(100, 100),
|
||||
name="osd-image",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
)
|
||||
self.label = Label(name="osd-label")
|
||||
self.add(self.osd_image)
|
||||
self._update_display(self.capslock.is_on)
|
||||
|
||||
def _connect_specific_signals(self):
|
||||
self.capslock.connect("state_changed", self._on_caps_lock_state_changed)
|
||||
|
||||
def _on_caps_lock_state_changed(self, _, is_on: bool):
|
||||
self._update_display(is_on)
|
||||
self.update()
|
||||
|
||||
def _update_display(self, is_on: bool):
|
||||
icon_path = "caps-lock.svg" if is_on else "caps-lock-off.svg"
|
||||
self.osd_image.set_from_file(
|
||||
get_relative_path(f"../../../assets/icons/misc/{icon_path}")
|
||||
)
|
||||
if is_on:
|
||||
self.add_style_class("capslock-on")
|
||||
self.remove_style_class("capslock-off")
|
||||
else:
|
||||
self.add_style_class("capslock-off")
|
||||
self.remove_style_class("capslock-on")
|
||||
|
||||
def update(self, *_):
|
||||
# We don't need a separate display update here as it's handled in the signal
|
||||
super().update()
|
||||
@@ -0,0 +1,40 @@
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.svg import Svg
|
||||
from fabric.utils import get_relative_path
|
||||
from .base import BaseOSDContainer
|
||||
from services.keyboard_layout import KeyboardLayout
|
||||
|
||||
|
||||
class KeyboardLayoutOSDContainer(BaseOSDContainer):
|
||||
def __init__(self, window, **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
self.keyboard_layout = KeyboardLayout.get_initial()
|
||||
self._setup_specific_components()
|
||||
self._connect_specific_signals()
|
||||
|
||||
def _setup_specific_components(self):
|
||||
self.osd_image = Svg(
|
||||
get_relative_path("../../../assets/icons/misc/keyboard-layout.svg"),
|
||||
size=(100, 100),
|
||||
name="osd-image",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
)
|
||||
self.label = Label(name="osd-label")
|
||||
self.add(self.osd_image)
|
||||
self.add(self.label)
|
||||
self.label.set_text(self.keyboard_layout.current_layout)
|
||||
|
||||
def _connect_specific_signals(self):
|
||||
self.keyboard_layout.connect("layout_changed", self._on_layout_changed)
|
||||
|
||||
def _on_layout_changed(self, service, layout: str):
|
||||
if not self.is_hovered():
|
||||
self.label.set_text(layout)
|
||||
self.update()
|
||||
|
||||
def update(self, *_):
|
||||
self.label.set_text(self.keyboard_layout.current_layout)
|
||||
super().update()
|
||||
@@ -0,0 +1,105 @@
|
||||
import math
|
||||
from fabric.audio import Audio
|
||||
from fabric.utils import get_relative_path
|
||||
from fabric.widgets.svg import Svg
|
||||
from fabric.widgets.scale import ScaleMark
|
||||
from .animated_scale import AnimatedScale
|
||||
from .base import BaseOSDContainer
|
||||
|
||||
|
||||
class MicrophoneOSDContainer(BaseOSDContainer):
|
||||
def __init__(self, window, **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
self.audio = Audio()
|
||||
self._last_volume = None
|
||||
self._last_muted = None
|
||||
self._setup_specific_components()
|
||||
self._connect_specific_signals()
|
||||
|
||||
def _setup_specific_components(self):
|
||||
self.osd_window_image = Svg(
|
||||
get_relative_path("../../../config/assets/icons/mic/microphone.svg"),
|
||||
size=(100, 100),
|
||||
name="osd-image",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
)
|
||||
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.add(self.osd_window_image)
|
||||
self.add(self.scale)
|
||||
|
||||
def _connect_specific_signals(self):
|
||||
self.audio.connect("notify::microphone", self._on_microphone_changed)
|
||||
if self.audio.microphone:
|
||||
self._connect_microphone_signals()
|
||||
|
||||
def _connect_microphone_signals(self):
|
||||
if self.audio.microphone:
|
||||
self.audio.microphone.connect("changed", self._on_microphone_stream_changed)
|
||||
self._sync_with_audio()
|
||||
|
||||
def _on_microphone_changed(self, *_):
|
||||
self._connect_microphone_signals()
|
||||
self.update()
|
||||
|
||||
def _on_microphone_stream_changed(self, *_):
|
||||
if self.audio.microphone:
|
||||
current_volume = round(self.audio.microphone.volume)
|
||||
current_muted = self.audio.microphone.muted
|
||||
if self._last_volume != current_volume or self._last_muted != current_muted:
|
||||
self._last_volume = current_volume
|
||||
self._last_muted = current_muted
|
||||
self.update()
|
||||
|
||||
def _sync_with_audio(self):
|
||||
if self.audio.microphone:
|
||||
self._last_volume = round(self.audio.microphone.volume)
|
||||
self._last_muted = self.audio.microphone.muted
|
||||
self.scale.set_value(self._last_volume)
|
||||
self._update_display()
|
||||
|
||||
def _update_display(self):
|
||||
if not self.audio.microphone:
|
||||
return
|
||||
|
||||
volume = (
|
||||
self._last_volume
|
||||
if self._last_volume is not None
|
||||
else round(self.audio.microphone.volume)
|
||||
)
|
||||
muted = (
|
||||
self._last_muted
|
||||
if self._last_muted is not None
|
||||
else self.audio.microphone.muted
|
||||
)
|
||||
|
||||
display_volume = 0 if (volume == 0 or muted) else volume
|
||||
level = (
|
||||
0 if display_volume == 0 else min(int(math.ceil(display_volume / 33)), 3)
|
||||
)
|
||||
|
||||
self.osd_window_image.set_from_file(
|
||||
get_relative_path(
|
||||
f"../../../config/assets/icons/mic/microphone-{level}.svg"
|
||||
)
|
||||
)
|
||||
|
||||
if muted or volume == 0:
|
||||
self.scale.add_style_class("muted")
|
||||
else:
|
||||
self.scale.remove_style_class("muted")
|
||||
|
||||
self.scale.animate_value(volume)
|
||||
|
||||
def update(self, *_):
|
||||
self._update_display()
|
||||
super().update()
|
||||
Reference in new issue
Block a user