(Init): Added shit
This commit is contained in:
171
nirimod/pages/startup.py
Normal file
171
nirimod/pages/startup.py
Normal file
@@ -0,0 +1,171 @@
|
||||
"""Startup Programs page."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
import shlex
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "4.0")
|
||||
gi.require_version("Adw", "1")
|
||||
from gi.repository import Adw, Gtk, GLib
|
||||
|
||||
from nirimod.kdl_parser import KdlNode
|
||||
from nirimod.pages.base import BasePage
|
||||
|
||||
|
||||
class StartupPage(BasePage):
|
||||
def build(self) -> Gtk.Widget:
|
||||
tb, header, _, content = self._make_toolbar_page("Startup Programs")
|
||||
self._content = content
|
||||
|
||||
|
||||
|
||||
self.refresh()
|
||||
return tb
|
||||
|
||||
def refresh(self):
|
||||
self._rebuild()
|
||||
|
||||
def _get_entries(self) -> list[KdlNode]:
|
||||
return [
|
||||
n
|
||||
for n in self._nodes
|
||||
if n.name in ("spawn-at-startup", "spawn-sh-at-startup")
|
||||
]
|
||||
|
||||
def _rebuild(self):
|
||||
# Clear existing content
|
||||
while True:
|
||||
child = self._content.get_first_child()
|
||||
if child is None:
|
||||
break
|
||||
self._content.remove(child)
|
||||
|
||||
entries = self._get_entries()
|
||||
|
||||
if not entries:
|
||||
status = Adw.StatusPage(
|
||||
title="No Startup Programs",
|
||||
description="Programs added here will launch automatically when niri starts.",
|
||||
icon_name="applications-system-symbolic",
|
||||
)
|
||||
|
||||
add_btn = Gtk.Button(label="Add Program")
|
||||
add_btn.add_css_class("pill")
|
||||
add_btn.add_css_class("suggested-action")
|
||||
add_btn.set_halign(Gtk.Align.CENTER)
|
||||
add_btn.connect("clicked", self._on_add)
|
||||
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||
box.set_valign(Gtk.Align.CENTER)
|
||||
box.set_vexpand(True)
|
||||
box.append(status)
|
||||
box.append(add_btn)
|
||||
|
||||
self._content.append(box)
|
||||
else:
|
||||
grp = Adw.PreferencesGroup(
|
||||
title="Startup Programs",
|
||||
description=f"{len(entries)} program{'s' if len(entries) != 1 else ''} configured to launch",
|
||||
)
|
||||
for i, entry in enumerate(entries):
|
||||
row = self._make_row(entry, i)
|
||||
grp.add(row)
|
||||
|
||||
self._content.append(grp)
|
||||
|
||||
# Also add a convenient button at the bottom
|
||||
add_btn = Gtk.Button(label="Add Another Program")
|
||||
add_btn.add_css_class("pill")
|
||||
add_btn.set_halign(Gtk.Align.CENTER)
|
||||
add_btn.set_margin_top(16)
|
||||
add_btn.connect("clicked", self._on_add)
|
||||
self._content.append(add_btn)
|
||||
|
||||
def _make_row(self, node: KdlNode, idx: int) -> Adw.ActionRow:
|
||||
cmd = " ".join(str(a) for a in node.args)
|
||||
is_sh = "sh" in node.name
|
||||
cmd_str = GLib.markup_escape_text(cmd) if cmd else "(empty)"
|
||||
|
||||
row = Adw.ActionRow(
|
||||
title=cmd_str or "(empty)",
|
||||
subtitle="Via shell (spawn-sh-at-startup)" if is_sh else "Launched directly",
|
||||
)
|
||||
row.set_activatable(True)
|
||||
row.connect("activated", lambda *_, i=idx: self._on_edit(i))
|
||||
|
||||
del_btn = Gtk.Button(icon_name="user-trash-symbolic")
|
||||
del_btn.set_valign(Gtk.Align.CENTER)
|
||||
del_btn.add_css_class("flat")
|
||||
del_btn.add_css_class("error")
|
||||
del_btn.set_tooltip_text("Remove startup entry")
|
||||
del_btn.connect("clicked", lambda *_, i=idx: self._on_delete(i))
|
||||
row.add_suffix(del_btn)
|
||||
return row
|
||||
|
||||
def _on_add(self, *_):
|
||||
self._show_dialog(None, -1)
|
||||
|
||||
def _on_edit(self, idx: int):
|
||||
entries = self._get_entries()
|
||||
if 0 <= idx < len(entries):
|
||||
self._show_dialog(entries[idx], idx)
|
||||
|
||||
def _on_delete(self, idx: int):
|
||||
entries = self._get_entries()
|
||||
if 0 <= idx < len(entries):
|
||||
self._nodes.remove(entries[idx])
|
||||
self._commit("remove startup entry")
|
||||
self._rebuild()
|
||||
|
||||
def _show_dialog(self, node: KdlNode | None, idx: int):
|
||||
dialog = Adw.AlertDialog(
|
||||
heading="Startup Program", body="Enter the command to launch at startup."
|
||||
)
|
||||
cmd_entry = Adw.EntryRow(title="Command")
|
||||
sh_switch = Adw.SwitchRow(title="Use shell (spawn-sh-at-startup)")
|
||||
if node:
|
||||
cmd_entry.set_text(" ".join(str(a) for a in node.args))
|
||||
sh_switch.set_active("sh" in node.name)
|
||||
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
|
||||
grp = Adw.PreferencesGroup()
|
||||
grp.add(cmd_entry)
|
||||
grp.add(sh_switch)
|
||||
box.append(grp)
|
||||
dialog.set_extra_child(box)
|
||||
|
||||
dialog.add_response("cancel", "Cancel")
|
||||
dialog.add_response("save", "Save")
|
||||
dialog.set_response_appearance("save", Adw.ResponseAppearance.SUGGESTED)
|
||||
|
||||
def _on_resp(d, r):
|
||||
if r != "save":
|
||||
return
|
||||
cmd = cmd_entry.get_text().strip()
|
||||
if not cmd:
|
||||
return
|
||||
is_sh = sh_switch.get_active()
|
||||
node_name = "spawn-sh-at-startup" if is_sh else "spawn-at-startup"
|
||||
if is_sh:
|
||||
# sh -c expects a single string; store the whole command as one arg
|
||||
args = [cmd]
|
||||
else:
|
||||
try:
|
||||
args = shlex.split(cmd)
|
||||
except ValueError:
|
||||
args = cmd.split()
|
||||
|
||||
new_node = KdlNode(node_name, args=args)
|
||||
entries = self._get_entries()
|
||||
if idx >= 0 and 0 <= idx < len(entries):
|
||||
i = self._nodes.index(entries[idx])
|
||||
self._nodes[i] = new_node
|
||||
else:
|
||||
self._nodes.append(new_node)
|
||||
self._commit("startup entry")
|
||||
self._rebuild()
|
||||
|
||||
dialog.connect("response", _on_resp)
|
||||
dialog.present(self._win)
|
||||
Reference in New Issue
Block a user