Compare commits

..

1 Commits

Author SHA1 Message Date
df2bef7685 fix: vinyl 2025-05-19 10:44:26 +02:00
3 changed files with 13 additions and 201 deletions

View File

@ -11,7 +11,6 @@ from fabric.utils import (
)
from .modules.bar import StatusBar
from .modules.window_fuzzy import FuzzyWindowFinder
from .modules.app_launcher import AppLauncher
tray = SystemTray(name="system-tray", spacing=4)
@ -19,11 +18,10 @@ river = get_river_connection()
dummy = Window(visible=False)
finder = FuzzyWindowFinder()
launcher = AppLauncher()
bar_windows = []
app = Application("bar", dummy, finder, launcher)
app = Application("bar", dummy, finder)
app.set_stylesheet_from_file(get_relative_path("styles/main.css"))

View File

@ -1,192 +0,0 @@
"""
example configuration shows how to make a simple
desktop applications launcher, this example doesn't involve
any styling (except a couple of basic style properties)
the purpose of this configuration is to show to to use
the given utils and mainly how using lazy executors might
make the configuration way more faster than it's supposed to be
"""
import operator
from collections.abc import Iterator
from dataclasses import dataclass
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.button import Button
from fabric.widgets.image import Image
from fabric.widgets.entry import Entry
from fabric.widgets.scrolledwindow import ScrolledWindow
from fabric.widgets.wayland import WaylandWindow as Window
from fabric.utils import DesktopApp, get_desktop_applications, idle_add, remove_handler
import subprocess
from time import sleep
import threading
@dataclass()
class CustomApp:
name: str
generic_name: str | None
display_name: str | None
description: str | None
executable: str | None
command_line: str | None
hidden: bool
def __init__(
self,
name,
display_name=None,
executable=None,
generic_name=None,
description=None,
command_line=None,
hidden=False,
):
self.name = name
self.generic_name = generic_name
self.display_name = display_name
self.description = description
self.executable = executable
self.command_line = command_line
self.hidden = hidden
def launch(self):
def background():
subprocess.run([self.command_line])
threading.Thread(target=background, daemon=True).start()
def get_icon_pixbuf(
self,
size: int = 48,
default_icon: str | None = "image-missing",
) -> None:
return None
class AppLauncher(Window):
def __init__(self, **kwargs):
super().__init__(
layer="top",
anchor="center",
exclusivity="none",
keyboard_mode="on-demand",
visible=False,
all_visible=False,
**kwargs,
)
self._arranger_handler: int = 0
self._all_apps = get_desktop_applications()
self._custom_apps = [
CustomApp("Screenshot Clipboard", command_line="grim2clip")
]
self.viewport = Box(spacing=2, orientation="v")
self.search_entry = Entry(
placeholder="Search Applications...",
h_expand=True,
notify_text=lambda entry, *_: self.arrange_viewport(entry.get_text()),
)
self.scrolled_window = ScrolledWindow(
min_content_size=(280, 320),
max_content_size=(280 * 2, 320),
child=self.viewport,
)
self.add(
Box(
spacing=2,
orientation="v",
style="margin: 2px",
children=[
# the header with the search entry
Box(
spacing=2,
orientation="h",
children=[
self.search_entry,
Button(
image=Image(icon_name="window-close"),
tooltip_text="Exit",
on_clicked=lambda *_: self.application.quit(),
),
],
),
# the actual slots holder
self.scrolled_window,
],
)
)
self.show_all()
def arrange_viewport(self, query: str = ""):
# reset everything so we can filter current viewport's slots...
# remove the old handler so we can avoid race conditions
remove_handler(self._arranger_handler) if self._arranger_handler else None
# remove all children from the viewport
self.viewport.children = []
combined_apps = self._all_apps + self._custom_apps
# make a new iterator containing the filtered apps
filtered_apps_iter = iter(
[
app
for app in combined_apps
if query.casefold()
in (
(app.display_name or "")
+ (" " + app.name + " ")
+ (app.generic_name or "")
).casefold()
]
)
should_resize = operator.length_hint(filtered_apps_iter) == len(self._all_apps)
# all aboard...
# start the process of adding slots with a lazy executor
# using this method makes the process of adding slots way more less
# resource expensive without blocking the main thread and resulting in a lock
self._arranger_handler = idle_add(
lambda *args: self.add_next_application(*args)
or (self.resize_viewport() if should_resize else False),
filtered_apps_iter,
pin=True,
)
return False
def add_next_application(self, apps_iter: Iterator[DesktopApp]):
if not (app := next(apps_iter, None)):
return False
self.viewport.add(self.bake_application_slot(app))
return True
def resize_viewport(self):
self.scrolled_window.set_min_content_width(
self.viewport.get_allocation().width # type: ignore
)
return False
def bake_application_slot(self, app: DesktopApp, **kwargs) -> Button:
return Button(
child=Box(
orientation="h",
spacing=12,
children=[
Image(pixbuf=app.get_icon_pixbuf(), h_align="start", size=32),
Label(
label=app.display_name or "Unknown",
v_align="center",
h_align="center",
),
],
),
tooltip_text=app.description,
on_clicked=lambda *_: (self.hide(), app.launch()),
**kwargs,
)

View File

@ -25,10 +25,14 @@ class VinylButton(Box):
def __init__(
self,
active_command="""pw-link alsa_input.pci-0000_12_00.6.analog-stereo:capture_FL alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX0
pw-link alsa_input.pci-0000_12_00.6.analog-stereo:capture_FR alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX1""",
inactive_command="""pw-link -d alsa_input.pci-0000_12_00.6.analog-stereo:capture_FL alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX0
pw-link -d alsa_input.pci-0000_12_00.6.analog-stereo:capture_FR alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX1 """,
active_command=[
"pw-link alsa_input.pci-0000_12_00.6.analog-stereo:capture_FL alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX0",
"pw-link alsa_input.pci-0000_12_00.6.analog-stereo:capture_FR alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX1",
],
inactive_command=[
"pw-link -d alsa_input.pci-0000_12_00.6.analog-stereo:capture_FL alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX0",
"pw-link -d alsa_input.pci-0000_12_00.6.analog-stereo:capture_FR alsa_output.usb-BEHRINGER_UMC1820_A71E9E3E-00.multichannel-output:playback_AUX1 ",
],
**kwargs,
):
super().__init__(**kwargs)
@ -80,13 +84,15 @@ pw-link -d alsa_input.pci-0000_12_00.6.analog-stereo:capture_FR alsa_output.usb-
def _execute_active_command(self):
"""Execute shell command when button is activated"""
try:
subprocess.Popen(self._active_command, shell=True)
for cmd in self._active_command:
subprocess.Popen(cmd, shell=True)
except Exception as e:
print(f"Error executing active command: {e}")
def _execute_inactive_command(self):
"""Execute shell command when button is deactivated"""
try:
subprocess.Popen(self._inactive_command, shell=True)
for cmd in self._inactive_command:
subprocess.Popen(cmd, shell=True)
except Exception as e:
print(f"Error executing inactive command: {e}")