added sh runner
This commit is contained in:
1 parent
0152c689e6
commit
27d7aed6b9
5 files changed
+169
-4
No files matched your search
@@ -41,7 +41,7 @@ ttf-font-awesome, otf-font-awesome, ttf-material-symbols-variable-git, ttf-googl
|
||||
# ToDo
|
||||
|
||||
- [x] Add bluetooth widget
|
||||
- [ ] Add shrunner widget
|
||||
- [x] Add shrunner widget
|
||||
- [ ] Add wifi widget
|
||||
- [ ] Dashboard
|
||||
- [ ] Notification panel
|
||||
|
||||
@@ -10,6 +10,7 @@ from modules.launcher.widgets import (
|
||||
Cliphist,
|
||||
Emoji,
|
||||
PowerMenu,
|
||||
Sh,
|
||||
TodoManager,
|
||||
WallpaperSelector,
|
||||
)
|
||||
@@ -31,12 +32,14 @@ class Launcher(Window):
|
||||
self.cliphist = Cliphist()
|
||||
self.todo = TodoManager()
|
||||
self.bluetooth = BluetoothManager()
|
||||
self.sh = Sh()
|
||||
|
||||
# Initialize viewports as None
|
||||
self._emoji_viewport = None
|
||||
self._cliphist_viewport = None
|
||||
self._todo_viewport = None
|
||||
self._bluetooth_viewport = None
|
||||
self._sh_viewport = None
|
||||
|
||||
self.stack = Stack(
|
||||
name="launcher-content",
|
||||
@@ -52,6 +55,7 @@ class Launcher(Window):
|
||||
self.cliphist,
|
||||
self.todo,
|
||||
self.bluetooth,
|
||||
self.sh,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -104,19 +108,17 @@ class Launcher(Window):
|
||||
self.cliphist,
|
||||
self.todo,
|
||||
self.bluetooth,
|
||||
self.sh,
|
||||
]:
|
||||
widget.remove_style_class("open")
|
||||
|
||||
# Handle wallpaper viewport
|
||||
if widget == self.wallpapers:
|
||||
self.wallpapers.viewport.hide()
|
||||
self.wallpapers.viewport.set_property("name", None)
|
||||
|
||||
# Handle emoji viewport safely
|
||||
if widget == self.emoji and hasattr(widget, "viewport") and widget.viewport:
|
||||
widget.viewport.hide()
|
||||
|
||||
# Handle cliphist viewport safely
|
||||
if (
|
||||
widget == self.cliphist
|
||||
and hasattr(widget, "viewport")
|
||||
@@ -132,6 +134,9 @@ class Launcher(Window):
|
||||
widget.viewport.children = []
|
||||
widget.is_active = False
|
||||
|
||||
if widget == self.sh and widget.viewport:
|
||||
widget.viewport.hide()
|
||||
|
||||
style_classes = [
|
||||
"launcher",
|
||||
"wallpapers",
|
||||
@@ -140,6 +145,7 @@ class Launcher(Window):
|
||||
"cliphist",
|
||||
"todo",
|
||||
"bluetooth",
|
||||
"sh",
|
||||
]
|
||||
for style_class in style_classes:
|
||||
if self.stack.get_style_context().has_class(style_class):
|
||||
@@ -159,6 +165,7 @@ class Launcher(Window):
|
||||
"cliphist": self.cliphist,
|
||||
"todo": self.todo,
|
||||
"bluetooth": self.bluetooth,
|
||||
"sh": self.sh,
|
||||
}
|
||||
|
||||
style_classes = list(widgets.keys())
|
||||
@@ -213,6 +220,11 @@ class Launcher(Window):
|
||||
self.bluetooth.search_entry.grab_focus()
|
||||
self.bluetooth.device_manager.arrange_viewport()
|
||||
|
||||
elif widget == "sh":
|
||||
self.sh.open_launcher()
|
||||
self.sh.search_entry.set_text("")
|
||||
self.sh.search_entry.grab_focus()
|
||||
|
||||
else:
|
||||
if hasattr(self.wallpapers, "viewport"):
|
||||
self.wallpapers.viewport.hide()
|
||||
|
||||
@@ -4,5 +4,6 @@ from .cliphist import *
|
||||
from .dashboard import *
|
||||
from .emoji import *
|
||||
from .power import *
|
||||
from .sh import *
|
||||
from .todo import *
|
||||
from .wallpapers import *
|
||||
@@ -0,0 +1,152 @@
|
||||
import os
|
||||
import subprocess
|
||||
from typing import List
|
||||
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from gi.repository import GLib
|
||||
from snippets import MaterialIcon
|
||||
|
||||
CACHE_DIR = os.getenv(
|
||||
"XDG_CACHE_HOME", os.path.join(os.path.expanduser("~"), ".cache", "fabric")
|
||||
)
|
||||
BINS = os.path.join(CACHE_DIR, "binaries")
|
||||
|
||||
|
||||
class Sh(Box):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="shell-command-launcher",
|
||||
all_visible=False,
|
||||
visible=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self._arranger_handler: int = 0
|
||||
self.shell_command_manager = ShellCommandManager(self)
|
||||
self.viewport = None
|
||||
|
||||
self.search_entry = Entry(
|
||||
name="search-entry",
|
||||
placeholder="Search Commands...",
|
||||
h_expand=True,
|
||||
notify_text=lambda entry, *_: self.handle_search_input(entry.get_text()),
|
||||
on_activate=lambda entry, *_: self.handle_search_input(entry.get_text()),
|
||||
)
|
||||
|
||||
self.header_box = Box(
|
||||
name="header-box",
|
||||
spacing=10,
|
||||
orientation="h",
|
||||
children=[
|
||||
self.search_entry,
|
||||
Button(
|
||||
name="close-button",
|
||||
child=MaterialIcon("close"),
|
||||
tooltip_text="Exit",
|
||||
on_clicked=lambda *_: self.close_launcher(),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
self.launcher_box = Box(
|
||||
name="shell-command-launcher-box",
|
||||
spacing=10,
|
||||
orientation="v",
|
||||
h_expand=True,
|
||||
children=[self.header_box],
|
||||
)
|
||||
|
||||
self.add(self.launcher_box)
|
||||
|
||||
def open_launcher(self):
|
||||
if not self.viewport:
|
||||
self.viewport = Box(name="viewport", spacing=10, orientation="v")
|
||||
self.scrolled_window = ScrolledWindow(
|
||||
name="scrolled-window",
|
||||
spacing=10,
|
||||
h_scrollbar_policy="never",
|
||||
v_scrollbar_policy="never",
|
||||
child=self.viewport,
|
||||
)
|
||||
self.launcher_box.add(self.scrolled_window)
|
||||
|
||||
self.viewport.children = []
|
||||
self.shell_command_manager.show_shell_commands(self.viewport, "")
|
||||
|
||||
self.viewport.show()
|
||||
self.search_entry.grab_focus()
|
||||
|
||||
def close_launcher(self):
|
||||
GLib.spawn_command_line_async("fabric-cli exec quickbar 'launcher.close()'")
|
||||
|
||||
def handle_search_input(self, text: str):
|
||||
self.shell_command_manager.show_shell_commands(self.viewport, text)
|
||||
|
||||
|
||||
class ShellCommandManager:
|
||||
def __init__(self, launcher):
|
||||
self.launcher = launcher
|
||||
self.binaries = set()
|
||||
self.reload_binaries()
|
||||
|
||||
def reload_binaries(self):
|
||||
paths = os.getenv("PATH", "").split(":")
|
||||
self.binaries = set()
|
||||
|
||||
for path in paths:
|
||||
self.binaries.update(self.list_binaries_in_path(path))
|
||||
|
||||
with open(BINS, "w") as f:
|
||||
f.write("\n".join(self.binaries))
|
||||
|
||||
def list_binaries_in_path(self, path):
|
||||
if not os.path.exists(path):
|
||||
return []
|
||||
try:
|
||||
return [
|
||||
entry
|
||||
for entry in os.listdir(path)
|
||||
if os.access(os.path.join(path, entry), os.X_OK)
|
||||
]
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
def show_shell_commands(self, viewport, search_query: str):
|
||||
if not search_query:
|
||||
return
|
||||
|
||||
viewport.children = []
|
||||
|
||||
results = self.query_binaries(search_query)
|
||||
self.display_results(viewport, results)
|
||||
|
||||
def query_binaries(self, filter_str: str) -> List[str]:
|
||||
filter_str = filter_str.lower()
|
||||
filtered_bins = (cmd for cmd in self.binaries if filter_str in cmd)
|
||||
return list(dict.fromkeys(filtered_bins))[:16]
|
||||
|
||||
def display_results(self, viewport, results: List[str]):
|
||||
for command in results:
|
||||
button = Button(
|
||||
child=Label(
|
||||
label=command,
|
||||
v_align="center",
|
||||
h_align="start",
|
||||
),
|
||||
on_clicked=lambda _, cmd=command: self.execute_command(cmd),
|
||||
name="sh-item",
|
||||
)
|
||||
viewport.add(button)
|
||||
|
||||
def execute_command(self, cmd: str):
|
||||
try:
|
||||
output = subprocess.check_output(cmd, shell=True, text=True)
|
||||
print(output)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e.output)
|
||||
finally:
|
||||
self.launcher.set_visible(False)
|
||||
Whitespace-only changes.
Reference in new issue
Block a user