Replace layer-shell AppletWindow-based dropdown with Gtk.Menu approach. Adds hide signal for button state reset, dropdown_option factory, append/clear methods, and popup_at_widget/popup aliases.
125 lines
3.5 KiB
Python
125 lines
3.5 KiB
Python
from functools import partial
|
|
|
|
from fabric.utils import logger
|
|
from gi.repository import Gdk, Gtk
|
|
|
|
from utils.functions import run_command, thread
|
|
|
|
|
|
class ModusDropdown:
|
|
"""Reusable Gtk.Menu-based dropdown."""
|
|
|
|
def __init__(self, items=None):
|
|
self._menu = Gtk.Menu()
|
|
self._source_button = None
|
|
self._menu.connect("hide", self._on_menu_hide)
|
|
|
|
if items:
|
|
self._build(items)
|
|
|
|
@property
|
|
def menu(self):
|
|
return self._menu
|
|
|
|
def _build(self, items):
|
|
for entry in items:
|
|
item = self._create_item(entry)
|
|
if item is not None:
|
|
self._menu.append(item)
|
|
self._menu.show_all()
|
|
|
|
def _create_item(self, entry):
|
|
if entry is None:
|
|
return Gtk.SeparatorMenuItem()
|
|
|
|
if isinstance(entry, Gtk.Widget):
|
|
return entry
|
|
|
|
if isinstance(entry, dict):
|
|
return self._make_item(entry)
|
|
|
|
label, callback = entry[0], entry[1]
|
|
sensitive = entry[2] if len(entry) > 2 else True
|
|
item = Gtk.MenuItem.new_with_label(label)
|
|
if sensitive and callback:
|
|
item.connect("activate", callback)
|
|
elif not sensitive:
|
|
item.set_sensitive(False)
|
|
return item
|
|
|
|
def _make_item(self, entry):
|
|
label = entry.get("label", "")
|
|
accel = entry.get("accel")
|
|
command = entry.get("command")
|
|
callback = entry.get("callback")
|
|
sensitive = entry.get("sensitive", True)
|
|
|
|
if accel:
|
|
label = f"{label} {accel}"
|
|
|
|
item = Gtk.MenuItem.new_with_label(label)
|
|
if not sensitive:
|
|
item.set_sensitive(False)
|
|
elif callback or command:
|
|
item.connect(
|
|
"activate",
|
|
partial(self._activate, callback, command),
|
|
)
|
|
return item
|
|
|
|
@staticmethod
|
|
def _activate(callback, command, *_):
|
|
if callback:
|
|
callback()
|
|
if command:
|
|
thread(run_command, ["sh", "-c", command], timeout=30)
|
|
|
|
def _on_menu_hide(self, *_):
|
|
if not self._source_button:
|
|
return
|
|
try:
|
|
self._source_button.unset_state_flags(Gtk.StateFlags.ACTIVE)
|
|
self._source_button.queue_draw()
|
|
except Exception:
|
|
logger.exception("[ModusDropdown] failed to reset button state")
|
|
self._source_button = None
|
|
|
|
def append(self, item):
|
|
created = self._create_item(item)
|
|
if created is not None:
|
|
self._menu.append(created)
|
|
self._menu.show_all()
|
|
|
|
def clear(self):
|
|
for child in self._menu.get_children():
|
|
child.destroy()
|
|
|
|
def popup_at_widget(self, widget, source_button=None):
|
|
self._source_button = source_button or widget
|
|
self._menu.popup_at_widget(
|
|
widget,
|
|
Gdk.Gravity.SOUTH_WEST,
|
|
Gdk.Gravity.NORTH_WEST,
|
|
None,
|
|
)
|
|
|
|
def popup(self, widget, source_button=None):
|
|
self.popup_at_widget(widget, source_button)
|
|
|
|
|
|
def dropdown_option(label, accel=None, command=None, callback=None, sensitive=True):
|
|
"""Create a dropdown menu option dict.
|
|
|
|
Usage:
|
|
dropdown_option("Lock Screen", " L", "fabric-cli exec modus 'lock_screen.lock()'")
|
|
dropdown_option("Settings", callback=my_func)
|
|
dropdown_option("Disabled Item", sensitive=False)
|
|
"""
|
|
return {
|
|
"label": label,
|
|
"accel": accel,
|
|
"command": command,
|
|
"callback": callback,
|
|
"sensitive": sensitive,
|
|
}
|