Files
Modus/modules/launcher/components/tools.py
T
2025-04-25 06:33:51 +05:45

371 lines
12 KiB
Python

from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.button import Button
from fabric.utils.helpers import exec_shell_command_async, get_relative_path
import utils.icons as icons
from gi.repository import Gdk, GLib
import os
import subprocess
import threading
CAPTURE_SCRIPT = get_relative_path("../../../config/scripts/capture.sh")
GAMEMODE_SCRIPT = get_relative_path("../../../config/scripts/gamemode.sh")
class Toolbox(Box):
def __init__(self, **kwargs):
super().__init__(
name="toolbox",
orientation="h",
spacing=4,
v_align="center",
h_align="center",
visible=True,
**kwargs,
)
self.launcher = kwargs["launcher"]
self.btn_ssregion = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.ssregion),
on_clicked=self.ssregion,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
# Enable keyboard focus and connect events
self.btn_ssregion.set_can_focus(True)
self.btn_ssregion.connect("button-press-event", self.on_ssregion_click)
self.btn_ssregion.connect("key-press-event", self.on_ssregion_key)
self.btn_ssfull = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.ssfull),
on_clicked=self.ssfull,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
# Enable keyboard focus and connect events
self.btn_ssfull.set_can_focus(True)
self.btn_ssfull.connect("button-press-event", self.on_ssfull_click)
self.btn_ssfull.connect("key-press-event", self.on_ssfull_key)
self.btn_screenrecord = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.screenrecord),
on_clicked=self.screenrecord,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
self.btn_ocr = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.ocr),
on_clicked=self.ocr,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
self.btn_color = Button(
name="toolbox-button",
tooltip_text="Color Picker\nLeft Click: HEX\nMiddle Click: HSV\nRight Click: RGB\n\nKeyboard:\nEnter: HEX\nShift+Enter: RGB\nCtrl+Enter: HSV",
child=Label(name="button-bar-label", markup=icons.colorpicker),
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
self.btn_gamemode = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.gamemode),
on_clicked=self.gamemode,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
# Enable keyboard focus for the colorpicker button.
self.btn_color.set_can_focus(True)
# Connect both mouse and keyboard events.
self.btn_color.connect("button-press-event", self.colorpicker)
self.btn_color.connect("key_press_event", self.colorpicker_key)
self.btn_emoji = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.emoji),
on_clicked=self.emoji,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
self.btn_screenshots_folder = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.screenshots),
on_clicked=self.open_screenshots_folder,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
self.btn_recordings_folder = Button(
name="toolbox-button",
child=Label(name="button-label", markup=icons.recordings),
on_clicked=self.open_recordings_folder,
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
)
self.buttons = [
self.btn_ssregion,
self.btn_ssfull,
self.btn_screenshots_folder,
Box(
name="tool-sep",
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
),
self.btn_screenrecord,
self.btn_recordings_folder,
Box(
name="tool-sep",
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
),
self.btn_ocr,
self.btn_color,
Box(
name="tool-sep",
h_expand=False,
v_expand=False,
h_align="center",
v_align="center",
),
self.btn_gamemode,
self.btn_emoji,
]
for button in self.buttons:
self.add(button)
self.show_all()
# Start polling for process state every second.
self.recorder_timer_id = GLib.timeout_add_seconds(
1, self.update_screenrecord_state
)
def close_menu(self):
self.launcher.close()
# Action methods
def ssfull(self, *args):
exec_shell_command_async(f"bash {CAPTURE_SCRIPT} screenshot p")
self.close_menu()
def on_ssfull_click(self, button, event):
if event.type == Gdk.EventType.BUTTON_PRESS:
if event.button == 1: # Left click
self.ssfull()
elif event.button == 3: # Right click
exec_shell_command_async(
f"bash {CAPTURE_SCRIPT} screenshot p mockup")
self.close_menu()
return True
return False
def on_ssfull_key(self, widget, event):
if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}:
modifiers = event.get_state()
if modifiers & Gdk.ModifierType.SHIFT_MASK:
exec_shell_command_async(
f"bash {CAPTURE_SCRIPT} screenshot p mockup")
self.close_menu()
else:
self.ssfull()
return True
return False
def ssregion(self, *args):
exec_shell_command_async(f"bash {CAPTURE_SCRIPT} screenshot sf")
self.close_menu()
def on_ssregion_click(self, button, event):
if event.type == Gdk.EventType.BUTTON_PRESS:
if event.button == 1: # Left click
self.ssregion()
elif event.button == 3: # Right click
exec_shell_command_async(
f"bash {CAPTURE_SCRIPT} screenshot sf mockup")
self.close_menu()
return True
return False
def on_ssregion_key(self, widget, event):
if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}:
modifiers = event.get_state()
if modifiers & Gdk.ModifierType.SHIFT_MASK:
exec_shell_command_async(
f"bash {CAPTURE_SCRIPT} screenshot sf mockup")
self.close_menu()
else:
self.ssregion()
return True
return False
def screenrecord(self, *args):
# Launch screenrecord script in detached mode so that it remains running independently of this program.
exec_shell_command_async(
f"bash -c 'nohup bash {CAPTURE_SCRIPT} record > /dev/null 2>&1 & disown'"
)
self.close_menu()
def ocr(self, *args):
exec_shell_command_async(f"bash {CAPTURE_SCRIPT} ocr sf")
self.close_menu()
def gamemode(self, *args):
exec_shell_command_async(f"bash {GAMEMODE_SCRIPT}")
self.gamemode_check()
self.close_menu()
def gamemode_check(self):
def check():
try:
result = subprocess.run(
f"bash {GAMEMODE_SCRIPT} check",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
enabled = result.stdout == b"t\n"
except Exception:
enabled = False
def update_ui():
if enabled:
self.btn_gamemode.get_child().set_markup(icons.gamemode_off)
else:
self.btn_gamemode.get_child().set_markup(icons.gamemode)
return False
GLib.idle_add(update_ui)
return False
GLib.idle_add(lambda: threading.Thread(target=check).start())
return True
def ssregion(self, *args):
exec_shell_command_async(f"bash {CAPTURE_SCRIPT} screenshot sf")
self.close_menu()
def colorpicker(self, button, event):
if event.type == Gdk.EventType.BUTTON_PRESS:
cmd = {
1: "-hex", # Left click
2: "-hsv", # Middle click
3: "-rgb", # Right click
}.get(event.button)
if cmd:
exec_shell_command_async(
f"bash {
get_relative_path(
'../../../config/scripts/hyprpicker.sh')
} {cmd}"
)
self.close_menu()
def colorpicker_key(self, widget, event):
if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}:
modifiers = event.get_state()
cmd = "-hex" # Default
match modifiers & (
Gdk.ModifierType.SHIFT_MASK | Gdk.ModifierType.CONTROL_MASK
):
case Gdk.ModifierType.SHIFT_MASK:
cmd = "-rgb"
case Gdk.ModifierType.CONTROL_MASK:
cmd = "-hsv"
exec_shell_command_async(
f"bash {get_relative_path('../../../config/scripts/hyprpicker.sh')} {
cmd
}"
)
self.close_menu()
return True
return False
def update_screenrecord_state(self):
def check():
try:
result = subprocess.run(
"pgrep -f gpu-screen-recorder",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
running = result.returncode == 0
except Exception:
running = False
def update_ui():
if running:
self.btn_screenrecord.get_child().set_markup(icons.stop)
self.btn_screenrecord.add_style_class("recording")
else:
self.btn_screenrecord.get_child().set_markup(icons.screenrecord)
self.btn_screenrecord.remove_style_class("recording")
return False
GLib.idle_add(update_ui)
return False
GLib.idle_add(lambda: threading.Thread(target=check).start())
return True
def open_screenshots_folder(self, *args):
screenshots_dir = os.path.join(
os.environ.get("XDG_PICTURES_DIR",
os.path.expanduser("~/Pictures")),
"Screenshots",
)
# Create directory if it doesn't exist
os.makedirs(screenshots_dir, exist_ok=True)
exec_shell_command_async(f"xdg-open {screenshots_dir}")
self.close_menu()
def open_recordings_folder(self, *args):
recordings_dir = os.path.join(
os.environ.get("XDG_VIDEOS_DIR", os.path.expanduser("~/Videos")),
"Recordings",
)
# Create directory if it doesn't exist
os.makedirs(recordings_dir, exist_ok=True)
exec_shell_command_async(f"xdg-open {recordings_dir}")
self.close_menu()
def emoji(self, *args):
self.launcher.open("emoji")