Files
2026-07-12 18:56:10 +05:45

177 lines
5.8 KiB
Python

from fabric.power_profiles import PowerProfiles
from fabric.utils import Gdk, idle_add, remove_handler
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.image import Image
from fabric.widgets.label import Label
from modules.launcher.base import LauncherPlugin
class PowerProfilePlugin(LauncherPlugin):
@property
def name(self) -> str:
return "power_profile"
@property
def icon(self) -> str:
return "power-profile-balanced" # Default icon
@property
def keywords(self) -> list[str]:
return ["power", "pp"]
def __init__(self, handler):
super().__init__(handler)
self._query_handler: int = 0
self.power_profiles = PowerProfiles()
self.power_profiles.connect("changed", self._on_power_profile_changed)
self.current_slots = []
def on_search(self, text: str) -> None:
self.query_power_profiles(text)
def stop(self):
if self._query_handler:
remove_handler(self._query_handler)
self._query_handler = 0
def _on_power_profile_changed(self, *args):
self._update_slot_statuses()
def _update_slot_statuses(self) -> None:
"""Update the status indicators of all current slots"""
for slot_info in self.current_slots:
profile_name = slot_info["profile_name"]
is_active = profile_name == self.power_profiles.active_profile
display_name = profile_name.replace("-", " ").title()
profile_text = f"{display_name} • Active" if is_active else display_name
slot_info["label"].set_text(profile_text)
if is_active:
slot_info["label"].add_style_class("power-profile-active")
else:
slot_info["label"].remove_style_class("power-profile-active")
def query_power_profiles(self, text: str) -> None:
self.handler.start("power-profiles")
self.current_slots = []
self._current_search_text = text
profiles = self.power_profiles.profiles
if not profiles:
self._show_no_profiles()
return
filtered_profiles = self._filter_profiles(text, profiles)
if not filtered_profiles:
return
self._query_handler = idle_add(
self._bake_next_profile_slot, iter(filtered_profiles), pin=True
)
def _filter_profiles(self, text: str, profiles) -> list:
return (
profiles
if not text.strip()
else [
profile for profile in profiles if text.lower() in str(profile).lower()
]
)
def _show_no_profiles(self) -> None:
self.handler.slot_ready(
Button(
style_classes="app-slot power-profile-no-profiles",
child=Box(
orientation="h",
spacing=12,
children=[
Image(icon_name="power-profile-balanced", size=32),
Label(
label="No power profiles available",
style_classes="power-profile-no-profiles-text",
h_align="center",
),
],
),
tooltip_text="No power profiles are available on this system",
),
"power-profiles",
)
self.handler.done()
def _bake_next_profile_slot(self, iterator) -> bool:
profile = next(iterator, None)
if profile is None:
idle_add(self.handler.done)
return False
self._create_profile_slot(profile)
return True
def _create_profile_slot(self, profile) -> None:
profile_name = (
profile.get("Profile", str(profile))
if isinstance(profile, dict)
else str(profile)
)
is_active = profile_name == self.power_profiles.active_profile
icon_name = f"power-profile-{profile_name}"
display_name = profile_name.replace("-", " ").title()
profile_text = f"{display_name} • Active" if is_active else display_name
label_widget = Label(
label=profile_text,
style_classes=f"power-profile-name {'power-profile-active' if is_active else ''}",
h_align="start",
v_align="center",
)
slot_content = Box(
orientation="h",
spacing=12,
children=[
Image(icon_name=icon_name, h_align="start", size=32),
label_widget,
],
)
slot_widget = Button(
style_classes="app-slot power-profile-slot",
child=slot_content,
tooltip_text=f"Click to set power profile to {display_name}\nCurrent: {self.power_profiles.active_profile}",
on_clicked=lambda *_: self._set_power_profile(profile_name),
)
slot_widget.connect(
"key-press-event",
lambda widget, event, p=profile_name: self._on_slot_key_press(
widget, event, p
),
)
self.current_slots.append(
{
"profile_name": profile_name,
"label": label_widget,
}
)
self.handler.slot_ready(slot_widget, "power-profiles")
def _set_power_profile(self, profile: str) -> None:
if profile != self.power_profiles.active_profile:
self.power_profiles.active_profile = profile
def _on_slot_key_press(self, widget, event, profile: str) -> bool:
if not event or event.keyval not in (Gdk.KEY_Return, Gdk.KEY_KP_Enter):
return False
self._set_power_profile(profile)
return True
def handle_external(self, command: str, args: str) -> None:
if args:
self._set_power_profile(args)