Compare commits
1 Commits
app-launch
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| df2bef7685 |
@ -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"))
|
||||
|
||||
|
||||
|
||||
@ -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,
|
||||
)
|
||||
@ -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}")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user