Files
Modus/modules/dock/components/controls.py
T
2025-06-26 16:10:17 +05:45

301 lines
12 KiB
Python

from fabric.audio.service import Audio
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.circularprogressbar import CircularProgressBar
from fabric.widgets.eventbox import EventBox
from fabric.widgets.label import Label
from fabric.widgets.overlay import Overlay
from gi.repository import Gdk, GLib
import config.data as data
import utils.icons as icons
from services.brightness import Brightness as BrightnessService
class Brightness(Box):
def __init__(self, **kwargs):
super().__init__(name="button-bar-brightness", **kwargs)
self.brightness = BrightnessService.get_initial()
if self.brightness.screen_brightness == -1:
self.destroy()
return
self.progress_bar = CircularProgressBar(
name="button-brightness",
size=28,
line_width=2,
start_angle=150,
end_angle=390,
)
self.brightness_label = Label(
name="brightness-label", markup=icons.brightness_high
)
self.brightness_button = Button(child=self.brightness_label)
self.event_box = EventBox(
events=["scroll", "smooth-scroll"],
child=Overlay(child=self.progress_bar, overlays=self.brightness_button),
)
self.event_box.connect("scroll-event", self.on_scroll)
self.add(self.event_box)
self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)
self._updating_from_brightness = False
self._pending_value = None
self._update_source_id = None
self.progress_bar.connect("notify::value", self.on_progress_value_changed)
self.brightness.connect("screen", self.on_brightness_changed)
self.on_brightness_changed()
def on_scroll(self, _, event):
if self.brightness.max_screen == -1:
return
current_brightness = self.brightness.screen_brightness
max_brightness = self.brightness.max_screen
step = max(1, int(max_brightness * 0.01)) # 1% of max brightness
if event.direction == Gdk.ScrollDirection.SMOOTH:
if hasattr(event, "delta_y") and abs(event.delta_y) > 0:
new_brightness = current_brightness - int(event.delta_y * step)
self.brightness.screen_brightness = new_brightness
elif event.direction == Gdk.ScrollDirection.UP:
self.brightness.screen_brightness = current_brightness + step
elif event.direction == Gdk.ScrollDirection.DOWN:
self.brightness.screen_brightness = current_brightness - step
def on_progress_value_changed(self, widget, pspec):
if self._updating_from_brightness:
return
new_norm = widget.value
new_brightness = int(new_norm * self.brightness.max_screen)
self._pending_value = new_brightness
if self._update_source_id is None:
self._update_source_id = GLib.timeout_add(
50, self._update_brightness_callback
)
def _update_brightness_callback(self):
if (
self._pending_value is not None
and self._pending_value != self.brightness.screen_brightness
):
self.brightness.screen_brightness = self._pending_value
self._pending_value = None
return True
else:
self._update_source_id = None
return False
def on_brightness_changed(self, *args):
if self.brightness.max_screen == -1:
return
normalized = self.brightness.screen_brightness / self.brightness.max_screen
self._updating_from_brightness = True
self.progress_bar.value = normalized
self._updating_from_brightness = False
brightness_percentage = int(normalized * 100)
if brightness_percentage >= 75:
self.brightness_label.set_markup(icons.brightness_high)
elif brightness_percentage >= 24:
self.brightness_label.set_markup(icons.brightness_medium)
else:
self.brightness_label.set_markup(icons.brightness_low)
self.set_tooltip_text(f"{brightness_percentage}%")
def destroy(self):
if self._update_source_id is not None:
GLib.source_remove(self._update_source_id)
super().destroy()
class Volume(Box):
def __init__(self, **kwargs):
super().__init__(name="button-bar-vol", **kwargs)
self.audio = Audio()
self.progress_bar = CircularProgressBar(
name="button-volume",
size=28,
line_width=2,
start_angle=150,
end_angle=390,
)
self.vol_label = Label(name="vol-label", markup=icons.vol_high)
self.vol_button = Button(on_clicked=self.toggle_mute, child=self.vol_label)
self.event_box = EventBox(
events=["scroll", "smooth-scroll"],
child=Overlay(child=self.progress_bar, overlays=self.vol_button),
)
self.audio.connect("notify::speaker", self.on_new_speaker)
if self.audio.speaker:
self.audio.speaker.connect("changed", self.on_speaker_changed)
self.event_box.connect("scroll-event", self.on_scroll)
self.add(self.event_box)
self.on_speaker_changed()
self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)
def on_new_speaker(self, *args):
if self.audio.speaker:
self.audio.speaker.connect("changed", self.on_speaker_changed)
self.on_speaker_changed()
def toggle_mute(self, event):
current_stream = self.audio.speaker
if current_stream:
current_stream.muted = not current_stream.muted
if current_stream.muted:
self.on_speaker_changed()
self.progress_bar.add_style_class("muted")
self.vol_label.add_style_class("muted")
else:
self.on_speaker_changed()
self.progress_bar.remove_style_class("muted")
self.vol_label.remove_style_class("muted")
def on_scroll(self, _, event):
if not self.audio.speaker:
return
current_volume = self.audio.speaker.volume
step = 1
if event.direction == Gdk.ScrollDirection.SMOOTH:
if hasattr(event, "delta_y") and abs(event.delta_y) > 0:
new_volume = current_volume - int(event.delta_y * step)
self.audio.speaker.volume = new_volume
# Remove delta_x handling
elif event.direction == Gdk.ScrollDirection.UP:
self.audio.speaker.volume = current_volume + step
elif event.direction == Gdk.ScrollDirection.DOWN:
self.audio.speaker.volume = current_volume - step
def on_speaker_changed(self, *_):
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 "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
self.progress_bar.value = self.audio.speaker.volume / 100
if self.audio.speaker.muted:
self.vol_label.set_markup(vol_mute_icon)
self.progress_bar.add_style_class("muted")
self.vol_label.add_style_class("muted")
self.set_tooltip_text("Muted")
return
else:
self.progress_bar.remove_style_class("muted")
self.vol_label.remove_style_class("muted")
self.set_tooltip_text(f"{round(self.audio.speaker.volume)}%")
if self.audio.speaker.volume > 74:
self.vol_label.set_markup(vol_high_icon)
elif self.audio.speaker.volume > 0:
self.vol_label.set_markup(vol_medium_icon)
else:
self.vol_label.set_markup(vol_off_icon)
class MicroPhone(Box):
def __init__(self, **kwargs):
super().__init__(name="button-bar-mic", **kwargs)
self.audio = Audio()
self.progress_bar = CircularProgressBar(
name="button-mic",
size=28,
line_width=2,
start_angle=150,
end_angle=390,
)
self.mic_label = Label(name="mic-label", markup=icons.mic)
self.mic_button = Button(on_clicked=self.toggle_mute, child=self.mic_label)
self.event_box = EventBox(
events=["scroll", "smooth-scroll"],
child=Overlay(child=self.progress_bar, overlays=self.mic_button),
)
self.audio.connect("notify::microphone", self.on_new_microphone)
if self.audio.microphone:
self.audio.microphone.connect("changed", self.on_microphone_changed)
self.event_box.connect("scroll-event", self.on_scroll)
self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)
self.add(self.event_box)
self.on_microphone_changed()
def on_new_microphone(self, *args):
if self.audio.microphone:
self.audio.microphone.connect("changed", self.on_microphone_changed)
self.on_microphone_changed()
def toggle_mute(self, event):
current_stream = self.audio.microphone
if current_stream:
current_stream.muted = not current_stream.muted
if current_stream.muted:
self.mic_label.set_markup(icons.mic_mute)
self.progress_bar.add_style_class("muted")
self.mic_label.add_style_class("muted")
else:
self.on_microphone_changed()
self.progress_bar.remove_style_class("muted")
self.mic_label.remove_style_class("muted")
def on_scroll(self, _, event):
if not self.audio.microphone:
return
if event.direction == Gdk.ScrollDirection.SMOOTH:
if hasattr(event, "delta_y") and abs(event.delta_y) > 0:
self.audio.microphone.volume -= event.delta_y
elif event.direction == Gdk.ScrollDirection.UP:
self.audio.microphone.volume += 1
elif event.direction == Gdk.ScrollDirection.DOWN:
self.audio.microphone.volume -= 1
def on_microphone_changed(self, *_):
if not self.audio.microphone:
return
self.progress_bar.value = self.audio.microphone.volume / 100
if self.audio.microphone.muted:
self.mic_label.set_markup(icons.mic_mute)
self.progress_bar.add_style_class("muted")
self.mic_label.add_style_class("muted")
self.set_tooltip_text("Muted")
return
else:
self.progress_bar.remove_style_class("muted")
self.mic_label.remove_style_class("muted")
self.set_tooltip_text(f"{round(self.audio.microphone.volume)}%")
if self.audio.microphone.volume > 74:
self.mic_label.set_markup(icons.mic)
elif self.audio.microphone.volume > 0:
self.mic_label.set_markup(icons.mic)
else:
self.mic_label.set_markup(icons.mic_mute)
class Controls(Box):
def __init__(self, **kwargs):
brightness = BrightnessService.get_initial()
children = []
if brightness.screen_brightness != -1:
children.append(Brightness())
children.extend([Volume(), MicroPhone()])
super().__init__(
name="control",
orientation="h" if not data.VERTICAL else "v",
spacing=4,
children=children,
**kwargs,
)
self.show_all()