Files
Modus/src/shared/window/dropdown.py
T
sankalp 8407c9685c refactor: rewrite ModusDropdown to use native Gtk.Menu
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.
2026-07-28 09:59:31 +05:45

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,
}