Files
niritings/nirimod/pages/workspaces.py
2026-05-29 00:41:12 +00:00

156 lines
5.1 KiB
Python

"""Workspaces page."""
from __future__ import annotations
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw, Gtk
from nirimod.kdl_parser import KdlNode, set_child_arg
from nirimod import niri_ipc
from nirimod.pages.base import BasePage
class WorkspacesPage(BasePage):
def build(self) -> Gtk.Widget:
tb, header, _, content = self._make_toolbar_page("Workspaces")
self._content = content
add_btn = Gtk.Button(icon_name="list-add-symbolic")
add_btn.add_css_class("flat")
add_btn.connect("clicked", self._on_add)
header.pack_end(add_btn)
self._grp = Adw.PreferencesGroup(
title="Named Workspaces",
description="Named workspaces open immediately at niri startup",
)
content.append(self._grp)
self.refresh()
return tb
def refresh(self):
self._rebuild()
def _get_ws_nodes(self) -> list[KdlNode]:
return [n for n in self._nodes if n.name == "workspace"]
def _rebuild(self):
parent = self._grp.get_parent()
if parent is None:
return
def _on_outputs(outputs_data):
ws_nodes = self._get_ws_nodes()
outputs = [o.get("name", "") for o in outputs_data]
output_model = Gtk.StringList.new(["(any)"] + outputs)
new_grp = Adw.PreferencesGroup(
title="Named Workspaces", description=f"{len(ws_nodes)} workspace(s)"
)
for i, ws in enumerate(ws_nodes):
row = self._make_ws_row(ws, i, outputs, output_model)
new_grp.add(row)
parent.remove(self._grp)
parent.append(new_grp)
self._grp = new_grp
niri_ipc.get_outputs(_on_outputs)
def _make_ws_row(
self, ws: KdlNode, idx: int, outputs: list[str], output_model: Gtk.StringList
) -> Adw.ExpanderRow:
name = ws.args[0] if ws.args else f"workspace-{idx + 1}"
assigned_out = ws.child_arg("open-on-output") or ""
exp = Adw.ExpanderRow(title=name)
name_row = Adw.EntryRow(title="Name")
name_row.set_text(str(name))
name_row.set_show_apply_button(True)
name_row.connect("apply", lambda r, i=idx: self._rename_ws(i, r.get_text()))
exp.add_row(name_row)
out_row = Adw.ComboRow(title="Open on Output")
out_list = ["(any)"] + outputs
out_row.set_model(Gtk.StringList.new(out_list))
if assigned_out in outputs:
out_row.set_selected(out_list.index(assigned_out))
out_row.connect(
"notify::selected",
lambda r, _, i=idx, ol=out_list: self._set_ws_output(
i, ol[r.get_selected()]
),
)
exp.add_row(out_row)
del_btn = Gtk.Button(icon_name="user-trash-symbolic")
del_btn.add_css_class("flat")
del_btn.add_css_class("error")
del_btn.set_valign(Gtk.Align.CENTER)
del_btn.connect("clicked", lambda *_, i=idx: self._on_delete(i))
exp.add_suffix(del_btn)
return exp
def _on_add(self, *_):
dialog = Adw.AlertDialog(
heading="Add Workspace", body="Enter a name for the new workspace."
)
entry = Adw.EntryRow(title="Workspace Name")
grp = Adw.PreferencesGroup()
grp.add(entry)
dialog.set_extra_child(grp)
dialog.add_response("cancel", "Cancel")
dialog.add_response("add", "Add")
dialog.set_response_appearance("add", Adw.ResponseAppearance.SUGGESTED)
def _on_resp(d, r):
if r != "add":
return
name = entry.get_text().strip()
if not name:
return
node = KdlNode("workspace", args=[name])
ws_nodes = self._get_ws_nodes()
if ws_nodes:
last_idx = self._nodes.index(ws_nodes[-1])
self._nodes.insert(last_idx + 1, node)
else:
# If no workspaces, insert at the top
self._nodes.insert(0, node)
self._commit("add workspace")
self._rebuild()
dialog.connect("response", _on_resp)
dialog.present(self._win)
def _on_delete(self, idx: int):
ws_nodes = self._get_ws_nodes()
if 0 <= idx < len(ws_nodes):
self._nodes.remove(ws_nodes[idx])
self._commit("remove workspace")
self._rebuild()
def _rename_ws(self, idx: int, name: str):
ws_nodes = self._get_ws_nodes()
if 0 <= idx < len(ws_nodes) and name.strip():
ws_nodes[idx].args = [name.strip()]
self._commit("rename workspace")
self._rebuild()
def _set_ws_output(self, idx: int, output: str):
ws_nodes = self._get_ws_nodes()
if 0 <= idx < len(ws_nodes):
ws = ws_nodes[idx]
if output and output != "(any)":
set_child_arg(ws, "open-on-output", output)
else:
from nirimod.kdl_parser import remove_child
remove_child(ws, "open-on-output")
self._commit("workspace output")